405853f2wg7JXZJNltspMwOZJklxgw linux-2.4.29-xen-sparse/arch/xen/drivers/netif/frontend/Makefile
41ee5e8b_2rt-qHzbDXtIoBzOli0EA linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/Makefile
41ee5e8bUhF4tH7OoJaVbUxdXqneVw linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/backend/Makefile
-41ee5e8bYDQkjRVKnFn5uFyy0KreCw linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/backend/common.h
-41ee5e8bt7xeBUJqG5XJS-ofukdsgA linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/backend/control.c
-41ee5e8bSs3BGC7yegM_ek2Tn0Ahvw linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/backend/interface.c
-41ee5e8bglvqKvZSY5uJ5JGQejEwyQ linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/backend/main.c
41ee5e8bSPpxzhGO6TrY20TegW3cZg linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/frontend/Makefile
-41ee5e8ckZ9xVNvu9NHIZDK7JqApmQ linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/frontend/main.c
-41ee5e8ck9scpGirfqEZRARbGDyTXA linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/frontend/xhci.h
-41ee5e8c6mLxIx82KPsbpt_uts_vSA linux-2.4.29-xen-sparse/arch/xen/drivers/usbif/usbif.h
3e5a4e65lWzkiPXsZdzPt2RNnJGG1g linux-2.4.29-xen-sparse/arch/xen/kernel/Makefile
3e5a4e65_hqfuxtGG8IUy6wRM86Ecg linux-2.4.29-xen-sparse/arch/xen/kernel/entry.S
3e5a4e65Hy_1iUvMTPsNqGNXd9uFpg linux-2.4.29-xen-sparse/arch/xen/kernel/head.S
405853f6nbeazrNyEWNHBuoSg2PiPA linux-2.6.10-xen-sparse/drivers/xen/netfront/netfront.c
4108f5c1ppFXVpQzCOAZ6xXYubsjKA linux-2.6.10-xen-sparse/drivers/xen/privcmd/Makefile
3e5a4e65IUfzzMu2kZFlGEB8-rpTaA linux-2.6.10-xen-sparse/drivers/xen/privcmd/privcmd.c
+41ee5e8bYDQkjRVKnFn5uFyy0KreCw linux-2.6.10-xen-sparse/drivers/xen/usbback/common.h
+41ee5e8bt7xeBUJqG5XJS-ofukdsgA linux-2.6.10-xen-sparse/drivers/xen/usbback/control.c
+41ee5e8bSs3BGC7yegM_ek2Tn0Ahvw linux-2.6.10-xen-sparse/drivers/xen/usbback/interface.c
+41ee5e8bglvqKvZSY5uJ5JGQejEwyQ linux-2.6.10-xen-sparse/drivers/xen/usbback/usbback.c
+41ee5e8ckZ9xVNvu9NHIZDK7JqApmQ linux-2.6.10-xen-sparse/drivers/xen/usbfront/usbfront.c
+41ee5e8ck9scpGirfqEZRARbGDyTXA linux-2.6.10-xen-sparse/drivers/xen/usbfront/xhci.h
412f47e4RKD-R5IS5gEXvcT8L4v8gA linux-2.6.10-xen-sparse/include/asm-generic/pgtable.h
40f56239YAjS52QG2FIAQpHDZAdGHg linux-2.6.10-xen-sparse/include/asm-xen/asm-i386/desc.h
4107adf1E5O4ztGHNGMzCCNhcvqNow linux-2.6.10-xen-sparse/include/asm-xen/asm-i386/dma-mapping.h
41c0c412FLc0gunlJl91qMYscFtXVA xen/include/public/io/ioreq.h
40f5623cTZ80EwjWUBlh44A9F9i_Lg xen/include/public/io/netif.h
41d40e9b8zCk5VDqhVbuQyhc7G3lqA xen/include/public/io/ring.h
+41ee5e8c6mLxIx82KPsbpt_uts_vSA xen/include/public/io/usbif.h
4051db79512nOCGweabrFWO2M2h5ng xen/include/public/physdev.h
40589968wmhPmV5-ENbBYmMjnedgKw xen/include/public/sched_ctl.h
404f3d2eR2Owk-ZcGOx9ULGHg3nrww xen/include/public/trace.h
djm@kirby.fc.hp.com
gm281@boulderdash.cl.cam.ac.uk
gm281@tetrapod.cl.cam.ac.uk
+harry@dory.(none)
iap10@freefall.cl.cam.ac.uk
iap10@labyrinth.cl.cam.ac.uk
iap10@nidd.cl.cam.ac.uk
+++ /dev/null
-
-#ifndef __USBIF__BACKEND__COMMON_H__
-#define __USBIF__BACKEND__COMMON_H__
-
-#include <linux/config.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/rbtree.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/blkdev.h>
-#include <asm/io.h>
-#include <asm/setup.h>
-#include <asm/pgalloc.h>
-#include <asm-xen/ctrl_if.h>
-#include <asm-xen/hypervisor.h>
-
-#include "../usbif.h"
-
-#if 0
-#define ASSERT(_p) \
- if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
- __LINE__, __FILE__); *(int*)0=0; }
-#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
- __FILE__ , __LINE__ , ## _a )
-#else
-#define ASSERT(_p) ((void)0)
-#define DPRINTK(_f, _a...) ((void)0)
-#endif
-
-typedef struct usbif_priv_st usbif_priv_t;
-
-struct usbif_priv_st {
- /* Unique identifier for this interface. */
- domid_t domid;
- unsigned int handle;
- /* Physical parameters of the comms window. */
- unsigned long shmem_frame;
- unsigned int evtchn;
- int irq;
- /* Comms information. */
- usbif_t *usb_ring_base; /* ioremap()'ed ptr to shmem_frame. */
- USBIF_RING_IDX usb_req_cons; /* Request consumer. */
- USBIF_RING_IDX usb_resp_prod; /* Private version of resp. producer. */
- /* Private fields. */
- enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
- /*
- * DISCONNECT response is deferred until pending requests are ack'ed.
- * We therefore need to store the id from the original request.
- */
- u8 disconnect_rspid;
- usbif_priv_t *hash_next;
- struct list_head usbif_list;
- spinlock_t usb_ring_lock;
- atomic_t refcnt;
- atomic_t work_scheduled;
-
- struct work_struct work;
-};
-
-void usbif_create(usbif_be_create_t *create);
-void usbif_destroy(usbif_be_destroy_t *destroy);
-void usbif_connect(usbif_be_connect_t *connect);
-int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id);
-void usbif_disconnect_complete(usbif_priv_t *up);
-
-void usbif_release_port(usbif_be_release_port_t *msg);
-int usbif_claim_port(usbif_be_claim_port_t *msg);
-void usbif_release_ports(usbif_priv_t *up);
-
-usbif_priv_t *usbif_find(domid_t domid);
-#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
-#define usbif_put(_b) \
- do { \
- if ( atomic_dec_and_test(&(_b)->refcnt) ) \
- usbif_disconnect_complete(_b); \
- } while (0)
-
-
-void usbif_interface_init(void);
-void usbif_ctrlif_init(void);
-
-void usbif_deschedule(usbif_priv_t *usbif);
-
-irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs);
-
-#endif /* __USBIF__BACKEND__COMMON_H__ */
+++ /dev/null
-/******************************************************************************
- * arch/xen/drivers/usbif/backend/control.c
- *
- * Routines for interfacing with the control plane.
- *
- * Copyright (c) 2004, Keir Fraser
- */
-
-#include "common.h"
-
-static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
-{
- DPRINTK("Received usbif backend message, subtype=%d\n", msg->subtype);
-
- switch ( msg->subtype )
- {
- case CMSG_USBIF_BE_CREATE:
- if ( msg->length != sizeof(usbif_be_create_t) )
- goto parse_error;
- usbif_create((usbif_be_create_t *)&msg->msg[0]);
- break;
- case CMSG_USBIF_BE_DESTROY:
- if ( msg->length != sizeof(usbif_be_destroy_t) )
- goto parse_error;
- usbif_destroy((usbif_be_destroy_t *)&msg->msg[0]);
- break;
- case CMSG_USBIF_BE_CONNECT:
- if ( msg->length != sizeof(usbif_be_connect_t) )
- goto parse_error;
- usbif_connect((usbif_be_connect_t *)&msg->msg[0]);
- break;
- case CMSG_USBIF_BE_DISCONNECT:
- if ( msg->length != sizeof(usbif_be_disconnect_t) )
- goto parse_error;
- if ( !usbif_disconnect((usbif_be_disconnect_t *)&msg->msg[0],msg->id) )
- return; /* Sending the response is deferred until later. */
- break;
- case CMSG_USBIF_BE_CLAIM_PORT:
- if ( msg->length != sizeof(usbif_be_claim_port_t) )
- goto parse_error;
- usbif_claim_port((usbif_be_claim_port_t *)&msg->msg[0]);
- break;
- case CMSG_USBIF_BE_RELEASE_PORT:
- if ( msg->length != sizeof(usbif_be_release_port_t) )
- goto parse_error;
- usbif_release_port((usbif_be_release_port_t *)&msg->msg[0]);
- break;
- default:
- goto parse_error;
- }
-
- ctrl_if_send_response(msg);
- return;
-
- parse_error:
- DPRINTK("Parse error while reading message subtype %d, len %d\n",
- msg->subtype, msg->length);
- msg->length = 0;
- ctrl_if_send_response(msg);
-}
-
-void usbif_ctrlif_init(void)
-{
- ctrl_msg_t cmsg;
- usbif_be_driver_status_changed_t st;
-
- (void)ctrl_if_register_receiver(CMSG_USBIF_BE, usbif_ctrlif_rx,
- CALLBACK_IN_BLOCKING_CONTEXT);
-
- /* Send a driver-UP notification to the domain controller. */
- cmsg.type = CMSG_USBIF_BE;
- cmsg.subtype = CMSG_USBIF_BE_DRIVER_STATUS_CHANGED;
- cmsg.length = sizeof(usbif_be_driver_status_changed_t);
- st.status = USBIF_DRIVER_STATUS_UP;
- memcpy(cmsg.msg, &st, sizeof(st));
- ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
-}
+++ /dev/null
-/******************************************************************************
- * arch/xen/drivers/usbif/backend/interface.c
- *
- * USB device interface management.
- *
- * by Mark Williamson, Copyright (c) 2004
- */
-
-
-/******************************************************************************
- * arch/xen/drivers/blkif/backend/interface.c
- *
- * Block-device interface management.
- *
- * Copyright (c) 2004, Keir Fraser
- */
-
-#include "common.h"
-
-#define USBIF_HASHSZ 1024
-#define USBIF_HASH(_d) (((int)(_d))&(USBIF_HASHSZ-1))
-
-static kmem_cache_t *usbif_priv_cachep;
-static usbif_priv_t *usbif_priv_hash[USBIF_HASHSZ];
-
-usbif_priv_t *usbif_find(domid_t domid)
-{
- usbif_priv_t *up = usbif_priv_hash[USBIF_HASH(domid)];
- while ( (up != NULL ) && ( up->domid != domid ) )
- up = up->hash_next;
- return up;
-}
-
-static void __usbif_disconnect_complete(void *arg)
-{
- usbif_priv_t *usbif = (usbif_priv_t *)arg;
- ctrl_msg_t cmsg;
- usbif_be_disconnect_t disc;
-
- /*
- * These can't be done in usbif_disconnect() because at that point there
- * may be outstanding requests at the device whose asynchronous responses
- * must still be notified to the remote driver.
- */
- unbind_evtchn_from_irq(usbif->evtchn);
- vfree(usbif->usb_ring_base);
-
- /* Construct the deferred response message. */
- cmsg.type = CMSG_USBIF_BE;
- cmsg.subtype = CMSG_USBIF_BE_DISCONNECT;
- cmsg.id = usbif->disconnect_rspid;
- cmsg.length = sizeof(usbif_be_disconnect_t);
- disc.domid = usbif->domid;
- disc.status = USBIF_BE_STATUS_OKAY;
- memcpy(cmsg.msg, &disc, sizeof(disc));
-
- /*
- * Make sure message is constructed /before/ status change, because
- * after the status change the 'usbif' structure could be deallocated at
- * any time. Also make sure we send the response /after/ status change,
- * as otherwise a subsequent CONNECT request could spuriously fail if
- * another CPU doesn't see the status change yet.
- */
- mb();
- if ( usbif->status != DISCONNECTING )
- BUG();
- usbif->status = DISCONNECTED;
- mb();
-
- /* Send the successful response. */
- ctrl_if_send_response(&cmsg);
-}
-
-void usbif_disconnect_complete(usbif_priv_t *up)
-{
- INIT_WORK(&up->work, __usbif_disconnect_complete, (void *)up);
- schedule_work(&up->work);
-}
-
-void usbif_create(usbif_be_create_t *create)
-{
- domid_t domid = create->domid;
- usbif_priv_t **pup, *up;
-
- if ( (up = kmem_cache_alloc(usbif_priv_cachep, GFP_KERNEL)) == NULL )
- {
- DPRINTK("Could not create usbif: out of memory\n");
- create->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
- return;
- }
-
- memset(up, 0, sizeof(*up));
- up->domid = domid;
- up->status = DISCONNECTED;
- spin_lock_init(&up->usb_ring_lock);
- atomic_set(&up->refcnt, 0);
-
- pup = &usbif_priv_hash[USBIF_HASH(domid)];
- while ( *pup != NULL )
- {
- if ( (*pup)->domid == domid )
- {
- create->status = USBIF_BE_STATUS_INTERFACE_EXISTS;
- kmem_cache_free(usbif_priv_cachep, up);
- return;
- }
- pup = &(*pup)->hash_next;
- }
-
- up->hash_next = *pup;
- *pup = up;
-
- create->status = USBIF_BE_STATUS_OKAY;
-}
-
-void usbif_destroy(usbif_be_destroy_t *destroy)
-{
- domid_t domid = destroy->domid;
- usbif_priv_t **pup, *up;
-
- pup = &usbif_priv_hash[USBIF_HASH(domid)];
- while ( (up = *pup) != NULL )
- {
- if ( up->domid == domid )
- {
- if ( up->status != DISCONNECTED )
- goto still_connected;
- goto destroy;
- }
- pup = &up->hash_next;
- }
-
- destroy->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
- return;
-
- still_connected:
- destroy->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
- return;
-
- destroy:
- *pup = up->hash_next;
- usbif_release_ports(up);
- kmem_cache_free(usbif_priv_cachep, up);
- destroy->status = USBIF_BE_STATUS_OKAY;
-}
-
-void usbif_connect(usbif_be_connect_t *connect)
-{
- domid_t domid = connect->domid;
- unsigned int evtchn = connect->evtchn;
- unsigned long shmem_frame = connect->shmem_frame;
- struct vm_struct *vma;
- pgprot_t prot;
- int error;
- usbif_priv_t *up;
-
- up = usbif_find(domid);
- if ( unlikely(up == NULL) )
- {
- DPRINTK("usbif_connect attempted for non-existent usbif (%u)\n",
- connect->domid);
- connect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
- return;
- }
-
- if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
- {
- connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
- return;
- }
-
- prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED);
- error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
- shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
- prot, domid);
- if ( error != 0 )
- {
- if ( error == -ENOMEM )
- connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
- else if ( error == -EFAULT )
- connect->status = USBIF_BE_STATUS_MAPPING_ERROR;
- else
- connect->status = USBIF_BE_STATUS_ERROR;
- vfree(vma->addr);
- return;
- }
-
- if ( up->status != DISCONNECTED )
- {
- connect->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
- vfree(vma->addr);
- return;
- }
-
- up->evtchn = evtchn;
- up->irq = bind_evtchn_to_irq(evtchn);
- up->shmem_frame = shmem_frame;
- up->usb_ring_base = (usbif_t *)vma->addr;
- up->status = CONNECTED;
- usbif_get(up);
-
- request_irq(up->irq, usbif_be_int, 0, "usbif-backend", up);
-
- connect->status = USBIF_BE_STATUS_OKAY;
-}
-
-/* Remove URBs for this interface before destroying it. */
-void usbif_deschedule(usbif_priv_t *up)
-{
- remove_from_usbif_list(up);
-}
-
-int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id)
-{
- domid_t domid = disconnect->domid;
- usbif_priv_t *up;
-
- up = usbif_find(domid);
- if ( unlikely(up == NULL) )
- {
- DPRINTK("usbif_disconnect attempted for non-existent usbif"
- " (%u)\n", disconnect->domid);
- disconnect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
- return 1; /* Caller will send response error message. */
- }
-
- if ( up->status == CONNECTED )
- {
- up->status = DISCONNECTING;
- up->disconnect_rspid = rsp_id;
- wmb(); /* Let other CPUs see the status change. */
- free_irq(up->irq, up);
- usbif_deschedule(up);
- usbif_put(up);
- return 0; /* Caller should not send response message. */
- }
-
- disconnect->status = USBIF_BE_STATUS_OKAY;
- return 1;
-}
-
-void __init usbif_interface_init(void)
-{
- usbif_priv_cachep = kmem_cache_create("usbif_priv_cache",
- sizeof(usbif_priv_t),
- 0, 0, NULL, NULL);
- memset(usbif_priv_hash, 0, sizeof(usbif_priv_hash));
-}
+++ /dev/null
-/******************************************************************************
- * arch/xen/drivers/usbif/backend/main.c
- *
- * Backend for the Xen virtual USB driver - provides an abstraction of a
- * USB host controller to the corresponding frontend driver.
- *
- * by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge
- *
- * Based on arch/xen/drivers/blkif/backend/main.c
- * Copyright (c) 2003-2004, Keir Fraser & Steve Hand
- */
-
-#include "common.h"
-
-
-#include <linux/list.h>
-#include <linux/usb.h>
-#include <linux/spinlock.h>
-#include <linux/module.h>
-#include <linux/tqueue.h>
-
-/*
- * This is rather arbitrary.
- */
-#define MAX_PENDING_REQS 4
-#define BATCH_PER_DOMAIN 1
-
-static unsigned long mmap_vstart;
-
-/* Needs to be sufficiently large that we can map the (large) buffers
- * the USB mass storage driver wants. */
-#define MMAP_PAGES_PER_REQUEST \
- (128)
-#define MMAP_PAGES \
- (MAX_PENDING_REQS * MMAP_PAGES_PER_REQUEST)
-
-#define MMAP_VADDR(_req,_seg) \
- (mmap_vstart + \
- ((_req) * MMAP_PAGES_PER_REQUEST * PAGE_SIZE) + \
- ((_seg) * PAGE_SIZE))
-
-#define MIN(x,y) ( ( x < y ) ? x : y )
-
-static spinlock_t owned_ports_lock;
-LIST_HEAD(owned_ports);
-
-/* A list of these structures is used to track ownership of physical USB
- * ports. */
-typedef struct
-{
- usbif_priv_t *usbif_priv;
- char path[16];
- int guest_port;
- int enabled;
- struct list_head list;
- unsigned long guest_address; /* The USB device address that has been
- * assigned by the guest. */
- int dev_present; /* Is there a device present? */
- struct usb_device * dev;
- unsigned long ifaces; /* What interfaces are present on this device? */
-} owned_port_t;
-
-
-/*
- * Each outstanding request that we've passed to the lower device layers has a
- * 'pending_req' allocated to it. The request is complete, the specified
- * domain has a response queued for it, with the saved 'id' passed back.
- */
-typedef struct {
- usbif_priv_t *usbif_priv;
- usbif_iso_t *iso_sched;
- unsigned long id;
- int nr_pages;
- unsigned short operation;
- int status;
-} pending_req_t;
-
-/*
- * We can't allocate pending_req's in order, since they may complete out of
- * order. We therefore maintain an allocation ring. This ring also indicates
- * when enough work has been passed down -- at that point the allocation ring
- * will be empty.
- */
-static pending_req_t pending_reqs[MAX_PENDING_REQS];
-static unsigned char pending_ring[MAX_PENDING_REQS];
-static spinlock_t pend_prod_lock = SPIN_LOCK_UNLOCKED;
-
-/* NB. We use a different index type to differentiate from shared blk rings. */
-typedef unsigned int PEND_RING_IDX;
-#define MASK_PEND_IDX(_i) ((_i)&(MAX_PENDING_REQS-1))
-static PEND_RING_IDX pending_prod, pending_cons;
-#define NR_PENDING_REQS (MAX_PENDING_REQS - pending_prod + pending_cons)
-
-static int do_usb_io_op(usbif_priv_t *usbif, int max_to_do);
-static void make_response(usbif_priv_t *usbif, unsigned long id,
- unsigned short op, int st, int inband,
- unsigned long actual_length);
-static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long port);
-static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req);
-static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid);
-static owned_port_t *usbif_find_port(char *);
-
-
-void dump_port(owned_port_t *p)
-{
- printk("owned_port_t @ %p\n", p);
- printk(" usbif_priv @ %p\n", p->usbif_priv);
- printk(" path: %s\n", p->path);
- printk(" guest_port: %d\n", p->guest_port);
- printk(" guest_address: %ld\n", p->guest_address);
- printk(" dev_present: %d\n", p->dev_present);
- printk(" dev @ %p\n", p->dev);
- printk(" ifaces: 0x%lx\n", p->ifaces);
-}
-
-
-
-static void fast_flush_area(int idx, int nr_pages)
-{
- multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
- int i;
-
- for ( i = 0; i < nr_pages; i++ )
- {
- mcl[i].op = __HYPERVISOR_update_va_mapping;
- mcl[i].args[0] = MMAP_VADDR(idx, i) >> PAGE_SHIFT;
- mcl[i].args[1] = 0;
- mcl[i].args[2] = 0;
- }
-
- mcl[nr_pages-1].args[2] = UVMF_FLUSH_TLB;
- if ( unlikely(HYPERVISOR_multicall(mcl, nr_pages) != 0) )
- BUG();
-}
-
-
-/******************************************************************
- * USB INTERFACE SCHEDULER LIST MAINTENANCE
- */
-
-static struct list_head usbio_schedule_list;
-static spinlock_t usbio_schedule_list_lock;
-
-static int __on_usbif_list(usbif_priv_t *up)
-{
- return up->usbif_list.next != NULL;
-}
-
-void remove_from_usbif_list(usbif_priv_t *up)
-{
- unsigned long flags;
- if ( !__on_usbif_list(up) ) return;
- spin_lock_irqsave(&usbio_schedule_list_lock, flags);
- if ( __on_usbif_list(up) )
- {
- list_del(&up->usbif_list);
- up->usbif_list.next = NULL;
- usbif_put(up);
- }
- spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
-}
-
-static void add_to_usbif_list_tail(usbif_priv_t *up)
-{
- unsigned long flags;
- if ( __on_usbif_list(up) ) return;
- spin_lock_irqsave(&usbio_schedule_list_lock, flags);
- if ( !__on_usbif_list(up) && (up->status == CONNECTED) )
- {
- list_add_tail(&up->usbif_list, &usbio_schedule_list);
- usbif_get(up);
- }
- spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
-}
-
-
-/******************************************************************
- * COMPLETION CALLBACK -- Called as urb->complete()
- */
-
-static void maybe_trigger_usbio_schedule(void);
-
-static void __end_usb_io_op(struct urb *purb)
-{
- unsigned long flags;
- pending_req_t *pending_req;
- int pending_idx;
-
- pending_req = purb->context;
-
-/* printk("Completed for id = %p to 0x%lx - 0x%lx\n", pending_req->id, */
-/* virt_to_machine(purb->transfer_buffer), */
-/* virt_to_machine(purb->transfer_buffer) */
-/* + pending_req->nr_pages * PAGE_SIZE); */
-
- pending_idx = pending_req - pending_reqs;
-
- ASSERT(purb->actual_length <= purb->transfer_buffer_length);
- ASSERT(purb->actual_length <= pending_req->nr_pages * PAGE_SIZE);
-
- /* An error fails the entire request. */
- if ( purb->status )
- {
- printk("URB @ %p failed. Status %d\n", purb, purb->status);
- }
-
- if ( usb_pipetype(purb->pipe) == 0 )
- {
- int i;
- usbif_iso_t *sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, pending_req->nr_pages - 1);
-
- ASSERT(sched == pending_req->sched);
-
- // printk("writing back schedule at %p\n", sched);
-
- /* If we're dealing with an iso pipe, we need to copy back the schedule. */
- for ( i = 0; i < purb->number_of_packets; i++ )
- {
- sched[i].length = purb->iso_frame_desc[i].actual_length;
- ASSERT(sched[i].buffer_offset ==
- purb->iso_frame_desc[i].offset);
- sched[i].status = purb->iso_frame_desc[i].status;
- }
- }
-
- // printk("Flushing %d pages\n", pending_req->nr_pages);
- fast_flush_area(pending_req - pending_reqs, pending_req->nr_pages);
-
- kfree(purb->setup_packet);
-
- spin_lock_irqsave(&pending_req->usbif_priv->usb_ring_lock, flags);
- make_response(pending_req->usbif_priv, pending_req->id,
- pending_req->operation, pending_req->status, 0, purb->actual_length);
- spin_unlock_irqrestore(&pending_req->usbif_priv->usb_ring_lock, flags);
- usbif_put(pending_req->usbif_priv);
-
- usb_free_urb(purb);
-
- /* Free the pending request. */
- spin_lock_irqsave(&pend_prod_lock, flags);
- pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
- spin_unlock_irqrestore(&pend_prod_lock, flags);
-
- rmb();
-
- /* Check for anything still waiting in the rings, having freed a request... */
- maybe_trigger_usbio_schedule();
-}
-
-/******************************************************************
- * SCHEDULER FUNCTIONS
- */
-
-static DECLARE_WAIT_QUEUE_HEAD(usbio_schedule_wait);
-
-static int usbio_schedule(void *arg)
-{
- DECLARE_WAITQUEUE(wq, current);
-
- usbif_priv_t *up;
- struct list_head *ent;
-
- daemonize();
-
- for ( ; ; )
- {
- /* Wait for work to do. */
- add_wait_queue(&usbio_schedule_wait, &wq);
- set_current_state(TASK_INTERRUPTIBLE);
- if ( (NR_PENDING_REQS == MAX_PENDING_REQS) ||
- list_empty(&usbio_schedule_list) )
- schedule();
- __set_current_state(TASK_RUNNING);
- remove_wait_queue(&usbio_schedule_wait, &wq);
-
- /* Queue up a batch of requests. */
- while ( (NR_PENDING_REQS < MAX_PENDING_REQS) &&
- !list_empty(&usbio_schedule_list) )
- {
- ent = usbio_schedule_list.next;
- up = list_entry(ent, usbif_priv_t, usbif_list);
- usbif_get(up);
- remove_from_usbif_list(up);
- if ( do_usb_io_op(up, BATCH_PER_DOMAIN) )
- add_to_usbif_list_tail(up);
- usbif_put(up);
- }
- }
-}
-
-static void maybe_trigger_usbio_schedule(void)
-{
- /*
- * Needed so that two processes, who together make the following predicate
- * true, don't both read stale values and evaluate the predicate
- * incorrectly. Incredibly unlikely to stall the scheduler on x86, but...
- */
- smp_mb();
-
- if ( !list_empty(&usbio_schedule_list) )
- wake_up(&usbio_schedule_wait);
-}
-
-
-/******************************************************************************
- * NOTIFICATION FROM GUEST OS.
- */
-
-irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs)
-{
- usbif_priv_t *up = dev_id;
-
- smp_mb();
-
- add_to_usbif_list_tail(up);
-
- /* Will in fact /always/ trigger an io schedule in this case. */
- maybe_trigger_usbio_schedule();
-
- return IRQ_HANDLED;
-}
-
-
-
-/******************************************************************
- * DOWNWARD CALLS -- These interface with the usb-device layer proper.
- */
-
-static int do_usb_io_op(usbif_priv_t *up, int max_to_do)
-{
- usbif_t *usb_ring = up->usb_ring_base;
- usbif_request_t *req;
- USBIF_RING_IDX i, rp;
- int more_to_do = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&up->usb_ring_lock, flags);
-
- rp = usb_ring->req_prod;
- rmb(); /* Ensure we see queued requests up to 'rp'. */
-
- /* Take items off the comms ring, taking care not to overflow. */
- for ( i = up->usb_req_cons;
- (i != rp) && ((i-up->usb_resp_prod) != USBIF_RING_SIZE);
- i++ )
- {
- if ( (max_to_do-- == 0) || (NR_PENDING_REQS == MAX_PENDING_REQS) )
- {
- more_to_do = 1;
- break;
- }
-
- req = &usb_ring->ring[MASK_USBIF_IDX(i)].req;
-
- switch ( req->operation )
- {
- case USBIF_OP_PROBE:
- dispatch_usb_probe(up, req->id, req->port);
- break;
-
- case USBIF_OP_IO:
- /* Assemble an appropriate URB. */
- dispatch_usb_io(up, req);
- break;
-
- case USBIF_OP_RESET:
- dispatch_usb_reset(up, req->port);
- break;
-
- default:
- DPRINTK("error: unknown USB io operation [%d]\n",
- req->operation);
- make_response(up, req->id, req->operation, -EINVAL, 0, 0);
- break;
- }
- }
-
- up->usb_req_cons = i;
-
- spin_unlock_irqrestore(&up->usb_ring_lock, flags);
-
- return more_to_do;
-}
-
-static owned_port_t *find_guest_port(usbif_priv_t *up, int port)
-{
- unsigned long flags;
- struct list_head *l;
-
- spin_lock_irqsave(&owned_ports_lock, flags);
- list_for_each(l, &owned_ports)
- {
- owned_port_t *p = list_entry(l, owned_port_t, list);
- if(p->usbif_priv == up && p->guest_port == port)
- {
- spin_unlock_irqrestore(&owned_ports_lock, flags);
- return p;
- }
- }
- spin_unlock_irqrestore(&owned_ports_lock, flags);
-
- return NULL;
-}
-
-static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid)
-{
- owned_port_t *port = find_guest_port(up, portid);
- int ret = 0;
-
-
- /* Allowing the guest to actually reset the device causes more problems
- * than it's worth. We just fake it out in software but we will do a real
- * reset when the interface is destroyed. */
-
-#if 0
- printk("Reset port %d\n", portid);
-
- dump_port(port);
-#endif
-
- port->guest_address = 0;
- /* If there's an attached device then the port is now enabled. */
- if ( port->dev_present )
- port->enabled = 1;
- else
- port->enabled = 0;
-
- make_response(up, 0, USBIF_OP_RESET, ret, 0, 0);
-}
-
-static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long portid)
-{
- owned_port_t *port = find_guest_port(up, portid);
- int ret;
-
- if ( port != NULL )
- ret = port->dev_present;
- else
- {
- ret = -EINVAL;
- printk("dispatch_usb_probe(): invalid port probe request (port %ld)\n",
- portid);
- }
-
- /* Probe result is sent back in-band. Probes don't have an associated id
- * right now... */
- make_response(up, id, USBIF_OP_PROBE, ret, portid, 0);
-}
-
-owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req);
-
-static void dump_request(usbif_request_t *req)
-{
- printk("id = 0x%lx\n", req->id);
-
- printk("devnum %d\n", req->devnum);
- printk("endpoint 0x%x\n", req->endpoint);
- printk("direction %d\n", req->direction);
- printk("speed %d\n", req->speed);
- printk("pipe_type 0x%x\n", req->pipe_type);
- printk("transfer_buffer 0x%lx\n", req->transfer_buffer);
- printk("length 0x%lx\n", req->length);
- printk("transfer_flags 0x%lx\n", req->transfer_flags);
- printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
- req->setup[0], req->setup[1], req->setup[2], req->setup[3],
- req->setup[4], req->setup[5], req->setup[6], req->setup[7]);
- printk("iso_schedule = 0x%lx\n", req->iso_schedule);
- printk("num_iso %ld\n", req->num_iso);
-}
-
-void dump_urb(struct urb *urb)
-{
- printk("dumping urb @ %p\n", urb);
-
-#define DUMP_URB_FIELD(name, format) printk(" " # name " " format "\n", urb-> name)
-
- DUMP_URB_FIELD(pipe, "0x%x");
- DUMP_URB_FIELD(status, "%d");
- DUMP_URB_FIELD(transfer_flags, "0x%x");
- DUMP_URB_FIELD(transfer_buffer, "%p");
- DUMP_URB_FIELD(transfer_buffer_length, "%d");
- DUMP_URB_FIELD(actual_length, "%d");
-}
-
-
-static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
-{
- unsigned long buffer_mach;
- int i = 0, offset = 0,
- pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
- pending_req_t *pending_req;
- unsigned long remap_prot;
- multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
- struct urb *purb = NULL;
- owned_port_t *port;
- unsigned char *setup;
-
-// dump_request(req);
-
- if ( NR_PENDING_REQS == MAX_PENDING_REQS )
- {
- printk("usbback: Max requests already queued. Now giving up!\n");
-
- return;
- }
-
- port = find_port_for_request(up, req);
-
- if(port == NULL)
- {
- printk("No such device! (%d)\n", req->devnum);
- dump_request(req);
-
- make_response(up, req->id, req->operation, -ENODEV, 0, 0);
- return;
- }
-
- setup = kmalloc(8, GFP_ATOMIC | GFP_NOIO);
-
- if ( setup == NULL )
- goto no_mem;
-
- /* Copy request out for safety. */
- memcpy(setup, req->setup, 8);
-
- if( setup[0] == 0x0 && setup[1] == 0x5)
- {
- /* To virtualise the USB address space, we need to intercept
- * set_address messages and emulate. From the USB specification:
- * bmRequestType = 0x0;
- * Brequest = SET_ADDRESS (i.e. 0x5)
- * wValue = device address
- * wIndex = 0
- * wLength = 0
- * data = None
- */
- /* Store into the guest transfer buffer using cpu_to_le16 */
- port->guest_address = le16_to_cpu(*(u16 *)(setup + 2));
- /* Make a successful response. That was easy! */
-
- make_response(up, req->id, req->operation, 0, 0, 0);
-
- kfree(setup);
- return;
- }
- else if ( setup[0] == 0x0 && setup[1] == 0x9 )
- {
- /* The host kernel needs to know what device configuration is in use
- * because various error checks get confused otherwise. We just do
- * configuration settings here, under controlled conditions.
- */
- usb_set_configuration(port->dev, setup[2]);
-
- make_response(up, req->id, req->operation, 0, 0, 0);
-
- kfree(setup);
- return;
- }
-
- else if ( setup[0] == 0x1 && setup[1] == 0xB )
- {
- /* The host kernel needs to know what device interface is in use
- * because various error checks get confused otherwise. We just do
- * configuration settings here, under controlled conditions.
- */
- usb_set_interface(port->dev, (setup[4] | setup[5] << 8),
- (setup[2] | setup[3] << 8) );
-
- make_response(up, req->id, req->operation, 0, 0, 0);
-
- kfree(setup);
- return;
- }
-
- if ( ( req->transfer_buffer - (req->transfer_buffer & PAGE_MASK)
- + req->length )
- > MMAP_PAGES_PER_REQUEST * PAGE_SIZE )
- {
- printk("usbback: request of %d bytes too large, failing it\n", req->length);
- make_response(up, req->id, req->operation, -EINVAL, 0, 0);
- kfree(setup);
- return;
- }
-
- buffer_mach = req->transfer_buffer;
-
- if( buffer_mach == 0 )
- goto no_remap;
-
- ASSERT((req->length >> PAGE_SHIFT) <= MMAP_PAGES_PER_REQUEST);
- ASSERT(buffer_mach);
-
- /* Always map writeable for now. */
- remap_prot = _PAGE_PRESENT|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW;
-
- for ( i = 0, offset = 0; offset < req->length;
- i++, offset += PAGE_SIZE )
- {
- // printk("length = %d, offset = %d, looping!\n", req->length, offset);
-
- mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
- mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
- mcl[i].args[1] = ((buffer_mach & PAGE_MASK) + offset) | remap_prot;
- mcl[i].args[2] = 0;
- mcl[i].args[3] = up->domid;
-
- phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
- FOREIGN_FRAME((buffer_mach + offset) >> PAGE_SHIFT);
- // printk("i = %d\n", i);
-
- ASSERT(virt_to_machine(MMAP_VADDR(pending_idx, i))
- == buffer_mach + i << PAGE_SHIFT);
- }
-
- if ( req->pipe_type == 0 && req->num_iso > 0 ) /* Maybe schedule ISO... */
- {
- // printk("for iso, i = %d\n", i);
- /* Map in ISO schedule, if necessary. */
- mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
- mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
- mcl[i].args[1] = (req->iso_schedule & PAGE_MASK) | remap_prot;
- mcl[i].args[2] = 0;
- mcl[i].args[3] = up->domid;
-
- phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
- FOREIGN_FRAME(req->iso_schedule >> PAGE_SHIFT);
-
- // printk("Mapped iso at %p\n", MMAP_VADDR(pending_idx, i));
- i++;
- }
-
- // printk("Well we got this far!\n");
-
- if ( unlikely(HYPERVISOR_multicall(mcl, i) != 0) )
- BUG();
-
- {
- int j;
- for ( j = 0; j < i; j++ )
- {
- if ( unlikely(mcl[j].args[5] != 0) )
- {
- printk("invalid buffer %d -- could not remap it\n", j);
- fast_flush_area(pending_idx, i);
- printk("sending invalid descriptor\n");
- goto bad_descriptor;
- }
- }
- }
-
- no_remap:
-
- ASSERT(i <= MMAP_PAGES_PER_REQUEST);
- ASSERT(i * PAGE_SIZE >= req->length);
-
- /* We have to do this because some things might complete out of order. */
- pending_req = &pending_reqs[pending_idx];
- pending_req->usbif_priv= up;
- pending_req->id = req->id;
- pending_req->operation = req->operation;
- pending_req->nr_pages = i;
-
-
-
- pending_cons++;
-
- usbif_get(up);
-
- /* Fill out an actual request for the USB layer. */
- purb = usb_alloc_urb(req->num_iso);
-
- if ( purb == NULL )
- goto no_mem;
-
- purb->dev = port->dev;
- purb->context = pending_req;
- purb->transfer_buffer = (void *)MMAP_VADDR(pending_idx, 0) + (buffer_mach & ~PAGE_MASK);
- if(buffer_mach == 0)
- purb->transfer_buffer = NULL;
- purb->complete = __end_usb_io_op;
- purb->transfer_buffer_length = req->length;
- purb->transfer_flags = req->transfer_flags;
-
-/* if ( req->transfer_flags != 0 ) */
-/* dump_request(req); */
-
- purb->pipe = 0;
- purb->pipe |= req->direction << 7;
- purb->pipe |= port->dev->devnum << 8;
- purb->pipe |= req->speed << 26;
- purb->pipe |= req->pipe_type << 30;
- purb->pipe |= req->endpoint << 15;
-
- purb->number_of_packets = req->num_iso;
-
- /* Make sure there's always some kind of timeout. */
- purb->timeout = ( req->timeout > 0 ) ? (req->timeout * HZ) / 1000
- : 1000;
-
- purb->setup_packet = setup;
-
- if ( req->pipe_type == 0 ) /* ISO */
- {
- int j;
- usbif_iso_t *iso_sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, i - 1);
-
- // printk("Reading iso sched at %p\n", iso_sched);
-
- /* If we're dealing with an iso pipe, we need to copy in a schedule. */
- for ( j = 0; j < req->num_iso; j++ )
- {
- purb->iso_frame_desc[j].length = iso_sched[j].length;
- purb->iso_frame_desc[j].offset = iso_sched[j].buffer_offset;
- iso_sched[j].status = 0;
- }
- pending_req->iso_sched = iso_sched;
- }
-
- {
- int ret;
- ret = usb_submit_urb(purb);
-
- // dump_urb(purb);
-
- if ( ret != 0 )
- goto bad_descriptor; /* XXX free pending here! */
- }
-
- return;
-
- bad_descriptor:
- kfree ( setup );
- if ( purb != NULL )
- usb_free_urb(purb);
- make_response(up, req->id, req->operation, -EINVAL, 0, 0);
- return;
-
- no_mem:
- if ( setup != NULL )
- kfree(setup);
- make_response(up, req->id, req->operation, -ENOMEM, 0, 0);
- return;
-}
-
-
-
-/******************************************************************
- * MISCELLANEOUS SETUP / TEARDOWN / DEBUGGING
- */
-
-
-static void make_response(usbif_priv_t *up, unsigned long id,
- unsigned short op, int st, int inband,
- unsigned long length)
-{
- usbif_response_t *resp;
- unsigned long flags;
-
-#if 0
- printk("usbback: Sending response:\n");
- printk(" id = 0x%x\n", id);
- printk(" op = %d\n", op);
- printk(" status = %d\n", st);
- printk(" data = %d\n", inband);
- printk(" length = %d\n", length);
-#endif
-
- /* Place on the response ring for the relevant domain. */
- spin_lock_irqsave(&up->usb_ring_lock, flags);
- resp = &up->usb_ring_base->
- ring[MASK_USBIF_IDX(up->usb_resp_prod)].resp;
- resp->id = id;
- resp->operation = op;
- resp->status = st;
- resp->data = inband;
- resp->length = length;
- wmb(); /* Ensure other side can see the response fields. */
- up->usb_ring_base->resp_prod = ++up->usb_resp_prod;
- spin_unlock_irqrestore(&up->usb_ring_lock, flags);
-
- /* Kick the relevant domain. */
- notify_via_evtchn(up->evtchn);
-}
-
-/**
- * usbif_claim_port - claim devices on a port on behalf of guest
- *
- * Once completed, this will ensure that any device attached to that
- * port is claimed by this driver for use by the guest.
- */
-int usbif_claim_port(usbif_be_claim_port_t *msg)
-{
- owned_port_t *o_p;
-
- /* Sanity... */
- if ( usbif_find_port(msg->path) != NULL )
- {
- printk("usbback: Attempted to claim USB port "
- "we already own!\n");
- return -EINVAL;
- }
-
- spin_lock_irq(&owned_ports_lock);
-
- /* No need for a slab cache - this should be infrequent. */
- o_p = kmalloc(sizeof(owned_port_t), GFP_KERNEL);
-
- o_p->enabled = 0;
- o_p->usbif_priv = usbif_find(msg->domid);
- o_p->guest_port = msg->usbif_port;
- o_p->dev_present = 0;
- o_p->guest_address = 0; /* Default address. */
-
- strcpy(o_p->path, msg->path);
-
- list_add(&o_p->list, &owned_ports);
-
- printk("usbback: Claimed USB port (%s) for %d.%d\n", o_p->path,
- msg->domid, msg->usbif_port);
-
- spin_unlock_irq(&owned_ports_lock);
-
- /* Force a reprobe for unclaimed devices. */
- usb_scan_devices();
-
- return 0;
-}
-
-owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req)
-{
- unsigned long flags;
- struct list_head *port;
-
- /* I'm assuming this is not called from IRQ context - correct? I think
- * it's probably only called in response to control messages or plug events
- * in the USB hub kernel thread, so should be OK. */
- spin_lock_irqsave(&owned_ports_lock, flags);
- list_for_each(port, &owned_ports)
- {
- owned_port_t *p = list_entry(port, owned_port_t, list);
- if(p->usbif_priv == up && p->guest_address == req->devnum && p->enabled )
- {
-#if 0
- printk("Found port for devnum %d\n", req->devnum);
-
- dump_port(p);
-#endif
- return p;
- }
- }
- spin_unlock_irqrestore(&owned_ports_lock, flags);
-
- return NULL;
-}
-
-owned_port_t *usbif_find_port(char *path)
-{
- struct list_head *port;
- unsigned long flags;
-
- spin_lock_irqsave(&owned_ports_lock, flags);
- list_for_each(port, &owned_ports)
- {
- owned_port_t *p = list_entry(port, owned_port_t, list);
- if(!strcmp(path, p->path))
- {
- spin_unlock_irqrestore(&owned_ports_lock, flags);
- return p;
- }
- }
- spin_unlock_irqrestore(&owned_ports_lock, flags);
-
- return NULL;
-}
-
-
-static void *probe(struct usb_device *dev, unsigned iface,
- const struct usb_device_id *id)
-{
- owned_port_t *p;
-
- /* We don't care what the device is - if we own the port, we want it. We
- * don't deal with device-specifics in this driver, so we don't care what
- * the device actually is ;-) */
- if ( ( p = usbif_find_port(dev->devpath) ) != NULL )
- {
- printk("usbback: claimed device attached to owned port\n");
-
- p->dev_present = 1;
- p->dev = dev;
- set_bit(iface, &p->ifaces);
-
- return p->usbif_priv;
- }
- else
- printk("usbback: hotplug for non-owned port (%s), ignoring\n", dev->devpath);
-
-
- return NULL;
-}
-
-static void disconnect(struct usb_device *dev, void *usbif)
-{
- /* Note the device is removed so we can tell the guest when it probes. */
- owned_port_t *port = usbif_find_port(dev->devpath);
- port->dev_present = 0;
- port->dev = NULL;
- port->ifaces = 0;
-}
-
-
-struct usb_driver driver =
-{
- .owner = THIS_MODULE,
- .name = "Xen USB Backend",
- .probe = probe,
- .disconnect = disconnect,
- .id_table = NULL,
-};
-
-/* __usbif_release_port - internal mechanics for releasing a port */
-void __usbif_release_port(owned_port_t *p)
-{
- int i;
-
- for ( i = 0; p->ifaces != 0; i++)
- if ( p->ifaces & 1 << i )
- {
- usb_driver_release_interface(&driver, usb_ifnum_to_if(p->dev, i));
- clear_bit(i, &p->ifaces);
- }
- list_del(&p->list);
-
- /* Reset the real device. We don't simulate disconnect / probe for other
- * drivers in this kernel because we assume the device is completely under
- * the control of ourselves (i.e. the guest!). This should ensure that the
- * device is in a sane state for the next customer ;-) */
-/* if ( p->dev != NULL) */
-/* usb_reset_device(p->dev); */
-
- kfree(p);
-}
-
-
-/**
- * usbif_release_port - stop claiming devices on a port on behalf of guest
- */
-void usbif_release_port(usbif_be_release_port_t *msg)
-{
- owned_port_t *p;
-
- spin_lock_irq(&owned_ports_lock);
- p = usbif_find_port(msg->path);
- __usbif_release_port(p);
- spin_unlock_irq(&owned_ports_lock);
-}
-
-void usbif_release_ports(usbif_priv_t *up)
-{
- struct list_head *port, *tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&owned_ports_lock, flags);
- list_for_each_safe(port, tmp, &owned_ports)
- {
- owned_port_t *p = list_entry(port, owned_port_t, list);
- if ( p->usbif_priv == up )
- __usbif_release_port(p);
- }
- spin_unlock_irqrestore(&owned_ports_lock, flags);
-}
-
-static int __init usbif_init(void)
-{
- int i;
-
- if ( !(xen_start_info.flags & SIF_INITDOMAIN) &&
- !(xen_start_info.flags & SIF_USB_BE_DOMAIN) )
- return 0;
-
- INIT_LIST_HEAD(&owned_ports);
-
- usb_register(&driver);
-
- usbif_interface_init();
-
- if ( (mmap_vstart = allocate_empty_lowmem_region(MMAP_PAGES)) == 0 )
- BUG();
-
- pending_cons = 0;
- pending_prod = MAX_PENDING_REQS;
- memset(pending_reqs, 0, sizeof(pending_reqs));
- for ( i = 0; i < MAX_PENDING_REQS; i++ )
- pending_ring[i] = i;
-
- spin_lock_init(&usbio_schedule_list_lock);
- INIT_LIST_HEAD(&usbio_schedule_list);
-
- if ( kernel_thread(usbio_schedule, 0, CLONE_FS | CLONE_FILES) < 0 )
- BUG();
-
- usbif_ctrlif_init();
-
- spin_lock_init(&owned_ports_lock);
-
- printk("Xen USB Backend Initialised");
-
- return 0;
-}
-
-__initcall(usbif_init);
+++ /dev/null
-/*
- * Xen Virtual USB Frontend Driver
- *
- * This file contains the first version of the Xen virtual USB hub
- * that I've managed not to delete by mistake (3rd time lucky!).
- *
- * Based on Linux's uhci.c, original copyright notices are displayed
- * below. Portions also (c) 2004 Intel Research Cambridge
- * and (c) 2004 Mark Williamson
- *
- * Contact <mark.williamson@cl.cam.ac.uk> or
- * <xen-devel@lists.sourceforge.net> regarding this code.
- *
- * Still to be (maybe) implemented:
- * - multiple port
- * - multiple interfaces
- * - migration / backend restart support?
- * - unloading support
- *
- * Differences to a normal host controller:
- * - the backend does most of the mucky stuff so we don't have to do various
- * things that are necessary for a normal host controller (e.g. FSBR).
- * - we don't have any hardware, so status registers are simulated in software.
- */
-
-/*
- * Universal Host Controller Interface driver for USB.
- *
- * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
- *
- * (C) Copyright 1999 Linus Torvalds
- * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
- * (C) Copyright 1999 Randy Dunlap
- * (C) Copyright 1999 Georg Acher, acher@in.tum.de
- * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
- * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
- * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
- * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
- * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
- * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- *
- * Intel documents this fairly well, and as far as I know there
- * are no royalties or anything like that, but even so there are
- * people who decided that they want to do the same thing in a
- * completely different way.
- *
- * WARNING! The USB documentation is downright evil. Most of it
- * is just crap, written by a committee. You're better off ignoring
- * most of it, the important stuff is:
- * - the low-level protocol (fairly simple but lots of small details)
- * - working around the horridness of the rest
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#ifdef CONFIG_USB_DEBUG
-#define DEBUG
-#else
-#undef DEBUG
-#endif
-#include <linux/usb.h>
-
-#include <asm/uaccess.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "xhci.h"
-
-#include <linux/pm.h>
-
-#include "../../../../../drivers/usb/hcd.h"
-
-#include "../usbif.h"
-#include <asm/ctrl_if.h>
-#include <asm/xen-public/io/domain_controller.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.0"
-#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, Mark Williamson"
-#define DRIVER_DESC "Xen Virtual USB Host Controller Interface driver"
-
-/*
- * debug = 0, no debugging messages
- * debug = 1, dump failed URB's except for stalls
- * debug = 2, dump all failed URB's (including stalls)
- */
-#ifdef DEBUG
-static int debug = 1;
-#else
-static int debug = 0;
-#endif
-MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug level");
-static char *errbuf;
-#define ERRBUF_LEN (PAGE_SIZE * 8)
-
-static kmem_cache_t *xhci_up_cachep; /* urb_priv */
-
-static int rh_submit_urb(struct urb *urb);
-static int rh_unlink_urb(struct urb *urb);
-//static int xhci_get_current_frame_number(struct usb_device *dev);
-static int xhci_unlink_urb(struct urb *urb);
-static void xhci_unlink_generic(struct urb *urb);
-static void xhci_call_completion(struct urb *urb);
-static void xhci_drain_ring(void);
-
-#define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */
-
-struct xhci *xhci;
-
-enum { USBIF_STATE_CONNECTED = 2,
- USBIF_STATE_DISCONNECTED = 1,
- USBIF_STATE_CLOSED =0
-};
-
-static int awaiting_reset = 0;
-
-/**
- * xhci_construct_isoc - add isochronous information to a request
- */
-int xhci_construct_isoc(usbif_request_t *req, struct urb *urb)
-{
- usbif_iso_t *schedule;
- int i;
- struct urb_priv *urb_priv = urb->hcpriv;
-
- req->num_iso = urb->number_of_packets;
- schedule = (usbif_iso_t *)__get_free_page(GFP_KERNEL);
-
- if ( schedule == NULL )
- return -ENOMEM;
-
- for ( i = 0; i < req->num_iso; i++ )
- {
- schedule[i].buffer_offset = urb->iso_frame_desc[i].offset;
- schedule[i].length = urb->iso_frame_desc[i].length;
- }
-
- urb_priv->schedule = schedule;
- req->iso_schedule = virt_to_machine(schedule);
-
- return 0;
-}
-
-#define USBIF_RING_FULL ((xhci->usbif->req_prod - xhci->usb_resp_cons) == USBIF_RING_SIZE)
-
-static void dump_urb(struct urb *urb)
-{
- printk("dumping urb @ %p\n", urb);
-
- printk("hcpriv = %p\n", urb->hcpriv);
- printk("next = %p\n", urb->next);
- printk("dev = %p\n", urb->dev);
- printk("pipe = 0x%lx\n", urb->pipe);
- printk("status = %d\n", urb->status);
- printk("transfer_flags = 0x%lx\n", urb->transfer_flags);
- printk("transfer_buffer = %p\n", urb->transfer_buffer);
- printk("transfer_buffer_length = %d\n", urb->transfer_buffer_length);
- printk("actual_length = %d\n", urb->actual_length);
- printk("bandwidth = %d\n", urb->bandwidth);
- printk("setup_packet = %p\n", urb->setup_packet);
- if ( urb->setup_packet != NULL )
- printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
- urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
- urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
- printk("complete = %p\n", urb->complete);
- printk("interval = %d\n", urb->interval);
-
-}
-
-
-static int
-xhci_queue_req(struct urb *urb)
-{
- usbif_request_t *req;
- usbif_t *usbif = xhci->usbif;
-
-#if 0
- printk("usbif = %p, req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
- usbif, usbif->req_prod, virt_to_machine(&usbif->req_prod),
- usbif->resp_prod, xhci->usb_resp_cons);
-#endif
-
-
-/* printk("Usbif_priv %p, want IO at 0x%lx\n", urb->hcpriv, virt_to_machine(urb->transfer_buffer)); */
-
- if ( USBIF_RING_FULL )
- {
- printk("xhci_queue_req(): USB ring full, not queuing request\n");
- return -ENOBUFS;
- }
-
- /* Stick something in the shared communications ring. */
- req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
-
- req->operation = USBIF_OP_IO;
- req->port = 0; /* We don't care what the port is. */
- req->id = (unsigned long) urb->hcpriv;
- req->transfer_buffer = virt_to_machine(urb->transfer_buffer);
- req->devnum = usb_pipedevice(urb->pipe);
- req->direction = usb_pipein(urb->pipe);
- req->speed = usb_pipeslow(urb->pipe);
- req->pipe_type = usb_pipetype(urb->pipe);
- req->length = urb->transfer_buffer_length;
- req->transfer_flags = urb->transfer_flags;
- req->endpoint = usb_pipeendpoint(urb->pipe);
- req->speed = usb_pipeslow(urb->pipe);
- req->timeout = urb->timeout * (1000 / HZ);
-
- if ( usb_pipetype(urb->pipe) == 0 ) /* ISO */
- {
- int ret = xhci_construct_isoc(req, urb);
- if ( ret != 0 )
- return ret;
- }
-
- if(urb->setup_packet != NULL)
- memcpy(req->setup, urb->setup_packet, 8);
- else
- memset(req->setup, 0, 8);
-
- wmb();
-
- usbif->req_prod++;
-
- notify_via_evtchn(xhci->evtchn);
-
- // dump_urb(urb);
-
- return -EINPROGRESS;
-}
-
-static inline usbif_request_t *
-xhci_queue_probe(usbif_vdev_t port)
-{
- usbif_request_t *req;
- usbif_t *usbif = xhci->usbif;
-
-#if 0
- printk("queuing probe: req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
- usbif->req_prod, virt_to_machine(&usbif->req_prod),
- usbif->resp_prod, xhci->usb_resp_cons);
-#endif
-
- if ( USBIF_RING_FULL )
- {
- printk("xhci_queue_probe(): USB ring full, not queuing request\n");
- return NULL;
- }
-
- /* Stick something in the shared communications ring. */
- req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
-
- req->operation = USBIF_OP_PROBE;
- req->port = port;
- req->id = 0;
- req->transfer_buffer = 0;
- req->devnum = 0;
- req->direction = 0;
- req->speed = 0;
- req->pipe_type = 0;
- req->length = 0;
- req->transfer_flags = 0;
- req->endpoint = 0;
- req->speed = 0;
-
- wmb();
-
- usbif->req_prod++;
-
- notify_via_evtchn(xhci->evtchn);
-
- return req;
-}
-
-static int
-xhci_port_reset(usbif_vdev_t port)
-{
- usbif_request_t *req;
- usbif_t *usbif = xhci->usbif;
-
- /* We only reset one port at a time, so we only need one variable per
- * hub. */
- awaiting_reset = 1;
-
- /* Stick something in the shared communications ring. */
- req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
-
- req->operation = USBIF_OP_RESET;
- req->port = port;
-
- wmb();
-
- usbif->req_prod++;
-
- notify_via_evtchn(xhci->evtchn);
-
- while ( awaiting_reset > 0 )
- {
- mdelay(1);
- xhci_drain_ring();
- }
-
- return awaiting_reset;
-}
-
-static void xhci_show_resp(usbif_response_t *r)
-{
- printk("id=0x%lx, op=0x%x, data=0x%x, status=0x%x, length=0x%lx\n",
- r->id, r->operation, r->data, r->status, r->length);
-}
-
-
-/*
- * Only the USB core should call xhci_alloc_dev and xhci_free_dev
- */
-static int xhci_alloc_dev(struct usb_device *dev)
-{
- return 0;
-}
-
-static int xhci_free_dev(struct usb_device *dev)
-{
- return 0;
-}
-
-static inline void xhci_add_complete(struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- unsigned long flags;
-
- spin_lock_irqsave(&xhci->complete_list_lock, flags);
- list_add_tail(&urbp->complete_list, &xhci->complete_list);
- spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
-}
-
-/* When this returns, the owner of the URB may free its
- * storage.
- *
- * We spin and wait for the URB to complete before returning.
- */
-static void xhci_delete_urb(struct urb *urb)
-{
- struct urb_priv *urbp;
-
- urbp = urb->hcpriv;
-
- /* If there's no urb_priv structure for this URB then it can't have
- * been submitted at all. */
- if ( urbp == NULL )
- return;
-
- /* For now we just spin until the URB completes. It shouldn't take too
- * long and we don't expect to have to do this very often. */
- while ( urb->status == -EINPROGRESS )
- {
- xhci_drain_ring();
- mdelay(1);
- }
-
- /* Now we know that further transfers to the buffer won't
- * occur, so we can safely return. */
-}
-
-static struct urb_priv *xhci_alloc_urb_priv(struct urb *urb)
-{
- struct urb_priv *urbp;
-
- urbp = kmem_cache_alloc(xhci_up_cachep, SLAB_ATOMIC);
- if (!urbp) {
- err("xhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
- return NULL;
- }
-
- memset((void *)urbp, 0, sizeof(*urbp));
-
- urbp->inserttime = jiffies;
- urbp->urb = urb;
- urbp->dev = urb->dev;
-
- INIT_LIST_HEAD(&urbp->complete_list);
-
- urb->hcpriv = urbp;
-
- return urbp;
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-/* When is this called? Do we need to stop the transfer (as we
- * currently do)? */
-static void xhci_destroy_urb_priv(struct urb *urb)
-{
- struct urb_priv *urbp;
-
- urbp = (struct urb_priv *)urb->hcpriv;
- if (!urbp)
- return;
-
- if (!list_empty(&urb->urb_list))
- warn("xhci_destroy_urb_priv: urb %p still on xhci->urb_list or xhci->remove_list", urb);
-
- if (!list_empty(&urbp->complete_list))
- warn("xhci_destroy_urb_priv: urb %p still on xhci->complete_list", urb);
-
- kmem_cache_free(xhci_up_cachep, urb->hcpriv);
-
- urb->hcpriv = NULL;
-}
-
-/**
- * Try to find URBs in progress on the same pipe to the same device.
- *
- * MUST be called with xhci->urb_list_lock acquired
- */
-static struct urb *xhci_find_urb_ep(struct xhci *xhci, struct urb *urb)
-{
- struct list_head *tmp, *head;
-
- /* We don't match Isoc transfers since they are special */
- if (usb_pipeisoc(urb->pipe))
- return NULL;
-
- head = &xhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb *u = list_entry(tmp, struct urb, urb_list);
-
- tmp = tmp->next;
-
- if (u->dev == urb->dev && u->pipe == urb->pipe &&
- u->status == -EINPROGRESS)
- return u;
- }
-
- return NULL;
-}
-
-static int xhci_submit_urb(struct urb *urb)
-{
- int ret = -EINVAL;
- unsigned long flags;
- struct urb *eurb;
- int bustime;
-
-#if 0
- printk("submitting urb @ %p for dev @ %p, devnum = %d path %s\n",
- urb, urb->dev, urb->dev->devnum, urb->dev->devpath);
-#endif
-
- if (!urb)
- return -EINVAL;
-
- if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
- warn("xhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
- return -ENODEV;
- }
-
- if ( urb->dev->devpath == NULL )
- {
- printk("BARF!\n");
- BUG();
- }
-
-
-
- usb_inc_dev_use(urb->dev);
-
- spin_lock_irqsave(&xhci->urb_list_lock, flags);
- spin_lock(&urb->lock);
-
- if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
- urb->status == -ECONNABORTED) {
- dbg("xhci_submit_urb: urb not available to submit (status = %d)", urb->status);
- /* Since we can have problems on the out path */
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
- usb_dec_dev_use(urb->dev);
-
- return ret;
- }
-
- INIT_LIST_HEAD(&urb->urb_list);
- if (!xhci_alloc_urb_priv(urb)) {
- ret = -ENOMEM;
-
- goto out;
- }
-
- ( (struct urb_priv *)urb->hcpriv )->in_progress = 1;
-
- eurb = xhci_find_urb_ep(xhci, urb);
- if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
- ret = -ENXIO;
-
- goto out;
- }
-
- /* Short circuit the virtual root hub */
- if (urb->dev == xhci->rh.dev) {
- ret = rh_submit_urb(urb);
-
- goto out;
- }
-
- if ( usb_pipedevice(urb->pipe) == 1 )
- printk("dev = %p, dev->path = %s, rh.dev = %p, rh.dev.devnum = %d rh.dev->path = %s!\n",
- urb->dev, urb->dev->devpath, xhci->rh.dev, xhci->rh.dev->devnum, xhci->rh.dev->devpath);
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- ret = xhci_queue_req(urb);
- break;
- case PIPE_INTERRUPT:
- if (urb->bandwidth == 0) { /* not yet checked/allocated */
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0)
- ret = bustime;
- else {
- ret = xhci_queue_req(urb);
- if (ret == -EINPROGRESS)
- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
- }
- } else /* bandwidth is already set */
- ret = xhci_queue_req(urb);
- break;
- case PIPE_BULK:
- ret = xhci_queue_req(urb);
- break;
- case PIPE_ISOCHRONOUS:
- if (urb->bandwidth == 0) { /* not yet checked/allocated */
- if (urb->number_of_packets <= 0) {
- ret = -EINVAL;
- break;
- }
- bustime = usb_check_bandwidth(urb->dev, urb);
- if (bustime < 0) {
- ret = bustime;
- break;
- }
-
- ret = xhci_queue_req(urb);
- if (ret == -EINPROGRESS)
- usb_claim_bandwidth(urb->dev, urb, bustime, 1);
- } else /* bandwidth is already set */
- ret = xhci_queue_req(urb);
- break;
- }
-
-out:
- urb->status = ret;
-
- if (ret == -EINPROGRESS) {
- /* We use _tail to make find_urb_ep more efficient */
- list_add_tail(&urb->urb_list, &xhci->urb_list);
-
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- return 0;
- }
-
- xhci_unlink_generic(urb);
-
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- /* Only call completion if it was successful */
- if (!ret)
- xhci_call_completion(urb);
-
- return ret;
-}
-
-/*
- * Return the result of a transfer
- *
- * MUST be called with urb_list_lock acquired
- */
-static void xhci_transfer_result(struct xhci *xhci, struct urb *urb)
-{
- int ret = 0;
- unsigned long flags;
- struct urb_priv *urbp;
-
- /* The root hub is special */
- if (urb->dev == xhci->rh.dev)
- return;
-
- spin_lock_irqsave(&urb->lock, flags);
-
- urbp = (struct urb_priv *)urb->hcpriv;
-
- if ( ( (struct urb_priv *)urb->hcpriv )->in_progress )
- ret = -EINPROGRESS;
-
- if (urb->actual_length < urb->transfer_buffer_length) {
- if (urb->transfer_flags & USB_DISABLE_SPD) {
- ret = -EREMOTEIO;
- }
- }
-
- if (urb->status == -EPIPE)
- {
- ret = urb->status;
- /* endpoint has stalled - mark it halted */
- usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe),
- usb_pipeout(urb->pipe));
- }
-
- if ((debug == 1 && ret != 0 && ret != -EPIPE) ||
- (ret != 0 && debug > 1)) {
- /* Some debugging code */
- dbg("xhci_result_interrupt/bulk() failed with status %x",
- status);
- }
-
- if (ret == -EINPROGRESS)
- goto out;
-
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_CONTROL:
- case PIPE_BULK:
- case PIPE_ISOCHRONOUS:
- /* Release bandwidth for Interrupt or Isoc. transfers */
- /* Spinlock needed ? */
- if (urb->bandwidth)
- usb_release_bandwidth(urb->dev, urb, 1);
- xhci_unlink_generic(urb);
- break;
- case PIPE_INTERRUPT:
- /* Interrupts are an exception */
- if (urb->interval)
- goto out_complete;
-
- /* Release bandwidth for Interrupt or Isoc. transfers */
- /* Spinlock needed ? */
- if (urb->bandwidth)
- usb_release_bandwidth(urb->dev, urb, 0);
- xhci_unlink_generic(urb);
- break;
- default:
- info("xhci_transfer_result: unknown pipe type %d for urb %p\n",
- usb_pipetype(urb->pipe), urb);
- }
-
- /* Remove it from xhci->urb_list */
- list_del_init(&urb->urb_list);
-
-out_complete:
- xhci_add_complete(urb);
-
-out:
- spin_unlock_irqrestore(&urb->lock, flags);
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static void xhci_unlink_generic(struct urb *urb)
-{
- struct urb_priv *urbp = urb->hcpriv;
-
- /* We can get called when urbp allocation fails, so check */
- if (!urbp)
- return;
-
- /* ??? This function is now so minimal it doesn't do much. Do we really
- * need it? */
-
- xhci_delete_urb(urb);
-}
-
-static int xhci_unlink_urb(struct urb *urb)
-{
- unsigned long flags;
- struct urb_priv *urbp = urb->hcpriv;
-
- if (!urb)
- return -EINVAL;
-
- if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
- return -ENODEV;
-
- spin_lock_irqsave(&xhci->urb_list_lock, flags);
- spin_lock(&urb->lock);
-
- /* Release bandwidth for Interrupt or Isoc. transfers */
- /* Spinlock needed ? */
- if (urb->bandwidth) {
- switch (usb_pipetype(urb->pipe)) {
- case PIPE_INTERRUPT:
- usb_release_bandwidth(urb->dev, urb, 0);
- break;
- case PIPE_ISOCHRONOUS:
- usb_release_bandwidth(urb->dev, urb, 1);
- break;
- default:
- break;
- }
- }
-
- if (urb->status != -EINPROGRESS) {
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
- return 0;
- }
-
- list_del_init(&urb->urb_list);
-
- xhci_unlink_generic(urb);
-
- /* Short circuit the virtual root hub */
- if (urb->dev == xhci->rh.dev) {
- rh_unlink_urb(urb);
-
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- xhci_call_completion(urb);
- } else {
- if (urb->transfer_flags & USB_ASYNC_UNLINK) {
- urbp->status = urb->status = -ECONNABORTED;
-
- spin_lock(&xhci->urb_remove_list_lock);
-
- list_add(&urb->urb_list, &xhci->urb_remove_list);
-
- spin_unlock(&xhci->urb_remove_list_lock);
-
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- } else {
- urb->status = -ENOENT;
-
- spin_unlock(&urb->lock);
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- if (in_interrupt()) { /* wait at least 1 frame */
- static int errorcount = 10;
-
- if (errorcount--)
- dbg("xhci_unlink_urb called from interrupt for urb %p", urb);
- udelay(1000);
- } else
- schedule_timeout(1+1*HZ/1000);
-
- xhci_call_completion(urb);
- }
- }
-
- return 0;
-}
-
-
-struct usb_operations xhci_device_operations = {
- .allocate = xhci_alloc_dev,
- .deallocate = xhci_free_dev,
- /* It doesn't look like any drivers actually care what the frame number
- * is at the moment! If necessary, we could approximate the current
- * frame nubmer by passing it from the backend in response messages. */
- .get_frame_number = NULL,
- .submit_urb = xhci_submit_urb,
- .unlink_urb = xhci_unlink_urb
-};
-
-/* Virtual Root Hub */
-
-static __u8 root_hub_dev_des[] =
-{
- 0x12, /* __u8 bLength; */
- 0x01, /* __u8 bDescriptorType; Device */
- 0x00, /* __u16 bcdUSB; v1.0 */
- 0x01,
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
- 0x00, /* __u8 bDeviceSubClass; */
- 0x00, /* __u8 bDeviceProtocol; */
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
- 0x00, /* __u16 idVendor; */
- 0x00,
- 0x00, /* __u16 idProduct; */
- 0x00,
- 0x00, /* __u16 bcdDevice; */
- 0x00,
- 0x00, /* __u8 iManufacturer; */
- 0x02, /* __u8 iProduct; */
- 0x01, /* __u8 iSerialNumber; */
- 0x01 /* __u8 bNumConfigurations; */
-};
-
-
-/* Configuration descriptor */
-static __u8 root_hub_config_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x02, /* __u8 bDescriptorType; Configuration */
- 0x19, /* __u16 wTotalLength; */
- 0x00,
- 0x01, /* __u8 bNumInterfaces; */
- 0x01, /* __u8 bConfigurationValue; */
- 0x00, /* __u8 iConfiguration; */
- 0x40, /* __u8 bmAttributes;
- Bit 7: Bus-powered, 6: Self-powered,
- Bit 5 Remote-wakeup, 4..0: resvd */
- 0x00, /* __u8 MaxPower; */
-
- /* interface */
- 0x09, /* __u8 if_bLength; */
- 0x04, /* __u8 if_bDescriptorType; Interface */
- 0x00, /* __u8 if_bInterfaceNumber; */
- 0x00, /* __u8 if_bAlternateSetting; */
- 0x01, /* __u8 if_bNumEndpoints; */
- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
- 0x00, /* __u8 if_bInterfaceSubClass; */
- 0x00, /* __u8 if_bInterfaceProtocol; */
- 0x00, /* __u8 if_iInterface; */
-
- /* endpoint */
- 0x07, /* __u8 ep_bLength; */
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
- 0x00,
- 0xff /* __u8 ep_bInterval; 255 ms */
-};
-
-static __u8 root_hub_hub_des[] =
-{
- 0x09, /* __u8 bLength; */
- 0x29, /* __u8 bDescriptorType; Hub-descriptor */
- 0x02, /* __u8 bNbrPorts; */
- 0x00, /* __u16 wHubCharacteristics; */
- 0x00,
- 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
- 0x00, /* __u8 bHubContrCurrent; 0 mA */
- 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
- 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
-};
-
-/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
-static int rh_send_irq(struct urb *urb)
-{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
- xhci_port_t *ports = xhci->rh.ports;
- unsigned long flags;
- int i, len = 1;
- __u16 data = 0;
-
- spin_lock_irqsave(&urb->lock, flags);
- for (i = 0; i < xhci->rh.numports; i++) {
- /* MAW: No idea what the old code was doing here or why it worked.
- * This implementation sets a bit if anything at all has changed on the
- * port, as per USB spec 11.12 */
- data |= (ports[i].cs_chg || ports[i].pe_chg )
- ? (1 << (i + 1))
- : 0;
-
- len = (i + 1) / 8 + 1;
- }
-
- *(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
- urb->actual_length = len;
- urbp->status = 0;
-
- spin_unlock_irqrestore(&urb->lock, flags);
-
- if ((data > 0) && (xhci->rh.send != 0)) {
- dbg("root-hub INT complete: data: %x", data);
- xhci_call_completion(urb);
- }
-
- return 0;
-}
-
-/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
-static int rh_init_int_timer(struct urb *urb);
-
-static void rh_int_timer_do(unsigned long ptr)
-{
- struct urb *urb = (struct urb *)ptr;
- struct list_head list, *tmp, *head;
- unsigned long flags;
- int i;
-
- for ( i = 0; i < xhci->rh.numports; i++)
- xhci_queue_probe(i);
-
- if (xhci->rh.send)
- rh_send_irq(urb);
-
- INIT_LIST_HEAD(&list);
-
- spin_lock_irqsave(&xhci->urb_list_lock, flags);
- head = &xhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb *u = list_entry(tmp, struct urb, urb_list);
- struct urb_priv *up = (struct urb_priv *)u->hcpriv;
-
- tmp = tmp->next;
-
- spin_lock(&u->lock);
-
- /* Check if the URB timed out */
- if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
- list_del(&u->urb_list);
- list_add_tail(&u->urb_list, &list);
- }
-
- spin_unlock(&u->lock);
- }
- spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
-
- head = &list;
- tmp = head->next;
- while (tmp != head) {
- struct urb *u = list_entry(tmp, struct urb, urb_list);
-
- tmp = tmp->next;
-
- u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
- xhci_unlink_urb(u);
- }
-
- rh_init_int_timer(urb);
-}
-
-/* Root Hub INTs are polled by this timer */
-static int rh_init_int_timer(struct urb *urb)
-{
- xhci->rh.interval = urb->interval;
- init_timer(&xhci->rh.rh_int_timer);
- xhci->rh.rh_int_timer.function = rh_int_timer_do;
- xhci->rh.rh_int_timer.data = (unsigned long)urb;
- xhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
- add_timer(&xhci->rh.rh_int_timer);
-
- return 0;
-}
-
-#define OK(x) len = (x); break
-
-/* Root Hub Control Pipe */
-static int rh_submit_urb(struct urb *urb)
-{
- unsigned int pipe = urb->pipe;
- struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
- void *data = urb->transfer_buffer;
- int leni = urb->transfer_buffer_length;
- int len = 0;
- xhci_port_t *status;
- int stat = 0;
- int i;
- int retstatus;
- unsigned long flags;
-
- __u16 cstatus;
- __u16 bmRType_bReq;
- __u16 wValue;
- __u16 wIndex;
- __u16 wLength;
-
- if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
- xhci->rh.urb = urb;
- xhci->rh.send = 1;
- xhci->rh.interval = urb->interval;
- rh_init_int_timer(urb);
-
- return -EINPROGRESS;
- }
-
- bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8;
- wValue = le16_to_cpu(cmd->wValue);
- wIndex = le16_to_cpu(cmd->wIndex);
- wLength = le16_to_cpu(cmd->wLength);
-
- for (i = 0; i < 8; i++)
- xhci->rh.c_p_r[i] = 0;
-
- status = &xhci->rh.ports[wIndex - 1];
-
- spin_lock_irqsave(&xhci->rh.port_state_lock, flags);
-
- switch (bmRType_bReq) {
- /* Request Destination:
- without flags: Device,
- RH_INTERFACE: interface,
- RH_ENDPOINT: endpoint,
- RH_CLASS means HUB here,
- RH_OTHER | RH_CLASS almost ever means HUB_PORT here
- */
-
- case RH_GET_STATUS:
- *(__u16 *)data = cpu_to_le16(1);
- OK(2);
- case RH_GET_STATUS | RH_INTERFACE:
- *(__u16 *)data = cpu_to_le16(0);
- OK(2);
- case RH_GET_STATUS | RH_ENDPOINT:
- *(__u16 *)data = cpu_to_le16(0);
- OK(2);
- case RH_GET_STATUS | RH_CLASS:
- *(__u32 *)data = cpu_to_le32(0);
- OK(4); /* hub power */
- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
- cstatus = (status->cs_chg) |
- (status->pe_chg << 1) |
- (xhci->rh.c_p_r[wIndex - 1] << 4);
- retstatus = (status->ccs) |
- (status->pe << 1) |
- (status->susp << 2) |
- (status->pr << 8) |
- (1 << 8) | /* power on */
- (status->lsda << 9);
- *(__u16 *)data = cpu_to_le16(retstatus);
- *(__u16 *)(data + 2) = cpu_to_le16(cstatus);
- OK(4);
- case RH_CLEAR_FEATURE | RH_ENDPOINT:
- switch (wValue) {
- case RH_ENDPOINT_STALL:
- OK(0);
- }
- break;
- case RH_CLEAR_FEATURE | RH_CLASS:
- switch (wValue) {
- case RH_C_HUB_OVER_CURRENT:
- OK(0); /* hub power over current */
- }
- break;
- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case RH_PORT_ENABLE:
- status->pe = 0;
- OK(0);
- case RH_PORT_SUSPEND:
- status->susp = 0;
- OK(0);
- case RH_PORT_POWER:
- OK(0); /* port power */
- case RH_C_PORT_CONNECTION:
- status->cs_chg = 0;
- OK(0);
- case RH_C_PORT_ENABLE:
- status->pe_chg = 0;
- OK(0);
- case RH_C_PORT_SUSPEND:
- /*** WR_RH_PORTSTAT(RH_PS_PSSC); */
- OK(0);
- case RH_C_PORT_OVER_CURRENT:
- OK(0); /* port power over current */
- case RH_C_PORT_RESET:
- xhci->rh.c_p_r[wIndex - 1] = 0;
- OK(0);
- }
- break;
- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
- switch (wValue) {
- case RH_PORT_SUSPEND:
- status->susp = 1;
- OK(0);
- case RH_PORT_RESET:
- {
- int ret;
- xhci->rh.c_p_r[wIndex - 1] = 1;
- status->pr = 0;
- status->pe = 1;
- ret = xhci_port_reset(wIndex - 1);
- /* XXX MAW: should probably cancel queued transfers during reset... *\/ */
- if ( ret == 0 ) { OK(0); }
- else { return ret; }
- }
- break;
- case RH_PORT_POWER:
- OK(0); /* port power ** */
- case RH_PORT_ENABLE:
- status->pe = 1;
- OK(0);
- }
- break;
- case RH_SET_ADDRESS:
- printk("setting root hub device to %d\n", wValue);
- xhci->rh.devnum = wValue;
- OK(0);
- case RH_GET_DESCRIPTOR:
- switch ((wValue & 0xff00) >> 8) {
- case 0x01: /* device descriptor */
- len = min_t(unsigned int, leni,
- min_t(unsigned int,
- sizeof(root_hub_dev_des), wLength));
- memcpy(data, root_hub_dev_des, len);
- OK(len);
- case 0x02: /* configuration descriptor */
- len = min_t(unsigned int, leni,
- min_t(unsigned int,
- sizeof(root_hub_config_des), wLength));
- memcpy (data, root_hub_config_des, len);
- OK(len);
- case 0x03: /* string descriptors */
- len = usb_root_hub_string (wValue & 0xff,
- 0, "XHCI-alt",
- data, wLength);
- if (len > 0) {
- OK(min_t(int, leni, len));
- } else
- stat = -EPIPE;
- }
- break;
- case RH_GET_DESCRIPTOR | RH_CLASS:
- root_hub_hub_des[2] = xhci->rh.numports;
- len = min_t(unsigned int, leni,
- min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
- memcpy(data, root_hub_hub_des, len);
- OK(len);
- case RH_GET_CONFIGURATION:
- *(__u8 *)data = 0x01;
- OK(1);
- case RH_SET_CONFIGURATION:
- OK(0);
- case RH_GET_INTERFACE | RH_INTERFACE:
- *(__u8 *)data = 0x00;
- OK(1);
- case RH_SET_INTERFACE | RH_INTERFACE:
- OK(0);
- default:
- stat = -EPIPE;
- }
-
- spin_unlock_irqrestore(&xhci->rh.port_state_lock, flags);
-
- urb->actual_length = len;
-
- return stat;
-}
-
-/*
- * MUST be called with urb->lock acquired
- */
-static int rh_unlink_urb(struct urb *urb)
-{
- if (xhci->rh.urb == urb) {
- urb->status = -ENOENT;
- xhci->rh.send = 0;
- xhci->rh.urb = NULL;
- del_timer(&xhci->rh.rh_int_timer);
- }
- return 0;
-}
-
-static void xhci_call_completion(struct urb *urb)
-{
- struct urb_priv *urbp;
- struct usb_device *dev = urb->dev;
- int is_ring = 0, killed, resubmit_interrupt, status;
- struct urb *nurb;
- unsigned long flags;
-
- spin_lock_irqsave(&urb->lock, flags);
-
- urbp = (struct urb_priv *)urb->hcpriv;
- if (!urbp || !urb->dev) {
- spin_unlock_irqrestore(&urb->lock, flags);
- return;
- }
-
- killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
- urb->status == -ECONNRESET);
- resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
- urb->interval);
-
- nurb = urb->next;
- if (nurb && !killed) {
- int count = 0;
-
- while (nurb && nurb != urb && count < MAX_URB_LOOP) {
- if (nurb->status == -ENOENT ||
- nurb->status == -ECONNABORTED ||
- nurb->status == -ECONNRESET) {
- killed = 1;
- break;
- }
-
- nurb = nurb->next;
- count++;
- }
-
- if (count == MAX_URB_LOOP)
- err("xhci_call_completion: too many linked URB's, loop? (first loop)");
-
- /* Check to see if chain is a ring */
- is_ring = (nurb == urb);
- }
-
- status = urbp->status;
- if (!resubmit_interrupt || killed)
- /* We don't need urb_priv anymore */
- xhci_destroy_urb_priv(urb);
-
- if (!killed)
- urb->status = status;
-
- spin_unlock_irqrestore(&urb->lock, flags);
-
- if (urb->complete)
- urb->complete(urb);
-
- if (resubmit_interrupt)
- /* Recheck the status. The completion handler may have */
- /* unlinked the resubmitting interrupt URB */
- killed = (urb->status == -ENOENT ||
- urb->status == -ECONNABORTED ||
- urb->status == -ECONNRESET);
-
- if (resubmit_interrupt && !killed) {
- if ( urb->dev != xhci->rh.dev )
- xhci_queue_req(urb); /* XXX What if this fails? */
- /* Don't need to resubmit URBs for the virtual root dev. */
- } else {
- if (is_ring && !killed) {
- urb->dev = dev;
- xhci_submit_urb(urb);
- } else {
- /* We decrement the usage count after we're done */
- /* with everything */
- usb_dec_dev_use(dev);
- }
- }
-}
-
-static void xhci_finish_completion(void)
-{
- struct list_head *tmp, *head;
- unsigned long flags;
-
- spin_lock_irqsave(&xhci->complete_list_lock, flags);
- head = &xhci->complete_list;
- tmp = head->next;
- while (tmp != head) {
- struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
- struct urb *urb = urbp->urb;
-
- list_del_init(&urbp->complete_list);
- spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
-
- xhci_call_completion(urb);
-
- spin_lock_irqsave(&xhci->complete_list_lock, flags);
- head = &xhci->complete_list;
- tmp = head->next;
- }
- spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
-}
-
-void receive_usb_reset(usbif_response_t *resp)
-{
- awaiting_reset = resp->status;
- rmb();
-
-}
-
-void receive_usb_probe(usbif_response_t *resp)
-{
- spin_lock(&xhci->rh.port_state_lock);
-
- if ( resp->status > 0 )
- {
- if ( resp->status == 1 )
- {
-/* printk("hey hey, there's a device on port %d\n", resp->data); */
-
- /* If theres a device there and there wasn't one before there must
- * have been a connection status change. */
- if( xhci->rh.ports[resp->data].cs == 0 )
- {
- xhci->rh.ports[resp->data].cs = 1;
- xhci->rh.ports[resp->data].ccs = 1;
- xhci->rh.ports[resp->data].cs_chg = 1;
-/* printk("Look at device on port %d that wasn't there before\n", resp->data); */
- }
- }
- else
- printk("receive_usb_probe(): unexpected status %d for port %d\n",
- resp->status, resp->data);
- }
- else if ( resp->status < 0)
- printk("receive_usb_probe(): got error status %d\n", resp->status);
-
- spin_unlock(&xhci->rh.port_state_lock);
-}
-
-void receive_usb_io(usbif_response_t *resp)
-{
- struct urb_priv *urbp = (struct urb_priv *)resp->id;
- struct urb *urb = urbp->urb;
-
- urb->actual_length = resp->length;
- urb->status = resp->status;
- urbp->status = resp->status;
- urbp->in_progress = 0;
-
- if( usb_pipetype(urb->pipe) == 0 ) /* ISO */
- {
- int i;
-
- /* Copy ISO schedule results back in. */
-
- for ( i = 0; i < urb->number_of_packets; i++ )
- {
- urb->iso_frame_desc[i].status
- = urbp->schedule[i].status;
- urb->iso_frame_desc[i].actual_length
- = urbp->schedule[i].length;
- }
- free_page((unsigned long)urbp->schedule);
- }
-}
-
-static void xhci_drain_ring(void)
-{
- struct list_head *tmp, *head;
- usbif_t *usb_ring = xhci->usbif;
- usbif_response_t *resp;
- USBIF_RING_IDX i, rp;
-
- /* Walk the ring here to get responses, updating URBs to show what
- * completed. */
-
- rp = usb_ring->resp_prod;
- rmb(); /* Ensure we see queued requests up to 'rp'. */
-
- /* Take items off the comms ring, taking care not to overflow. */
- for ( i = xhci->usb_resp_cons;
- (i != rp) && ((i-usb_ring->req_prod) != USBIF_RING_SIZE);
- i++ )
- {
- resp = &usb_ring->ring[MASK_USBIF_IDX(i)].resp;
-
- /* May need to deal with batching and with putting a ceiling on
- the number dispatched for performance and anti-dos reasons */
-
-#if 0
- printk("usbfront: Processing response:\n");
- printk(" id = 0x%x\n", resp->id);
- printk(" op = %d\n", resp->operation);
- printk(" status = %d\n", resp->status);
- printk(" length = %d\n", resp->length);
-#endif
-
- switch ( resp->operation )
- {
- case USBIF_OP_PROBE:
- receive_usb_probe(resp);
- break;
-
- case USBIF_OP_IO:
- receive_usb_io(resp);
- break;
-
- case USBIF_OP_RESET:
- receive_usb_reset(resp);
- break;
-
- default:
- printk("error: unknown USB io operation response [%d]\n",
- usb_ring->ring[i].req.operation);
- break;
- }
- }
-
- xhci->usb_resp_cons = i;
-
- /* Walk the list of pending URB's to see which ones completed and do
- * callbacks, etc. */
- spin_lock(&xhci->urb_list_lock);
- head = &xhci->urb_list;
- tmp = head->next;
- while (tmp != head) {
-
- struct urb *urb = list_entry(tmp, struct urb, urb_list);
-
- tmp = tmp->next;
-
- /* Checks the status and does all of the magic necessary */
- xhci_transfer_result(xhci, urb);
- }
- spin_unlock(&xhci->urb_list_lock);
-
- xhci_finish_completion();
-}
-
-
-static void xhci_interrupt(int irq, void *__xhci, struct pt_regs *regs)
-{
- xhci_drain_ring();
-}
-
-static void free_xhci(struct xhci *xhci)
-{
- kfree(xhci);
-}
-
-/* /\* */
-/* * De-allocate all resources.. */
-/* *\/ */
-/* static void release_xhci(struct xhci *xhci) */
-/* { */
-/* if (xhci->irq >= 0) { */
-/* free_irq(xhci->irq, xhci); */
-/* xhci->irq = -1; */
-/* } */
-
-/* /\* Get the ring back from the backend domain. Then free it. Hmmmm. */
-/* * Lets ignore this for now - not particularly useful. *\/ */
-
-/* free_xhci(xhci); */
-/* } */
-
-/**
- * Initialise a new virtual root hub for a new USB device channel.
- */
-static int alloc_xhci(void)
-{
- int retval;
- struct usb_bus *bus;
-
- retval = -EBUSY;
-
- xhci = kmalloc(sizeof(*xhci), GFP_KERNEL);
- if (!xhci) {
- err("couldn't allocate xhci structure");
- retval = -ENOMEM;
- goto err_alloc_xhci;
- }
-
- /* Reset here so we don't get any interrupts from an old setup */
- /* or broken setup */
- // reset_hc(xhci);
-
-
- xhci->state = USBIF_STATE_CLOSED;
- xhci->is_suspended = 0;
-
- spin_lock_init(&xhci->urb_remove_list_lock);
- INIT_LIST_HEAD(&xhci->urb_remove_list);
-
- spin_lock_init(&xhci->urb_list_lock);
- INIT_LIST_HEAD(&xhci->urb_list);
-
- spin_lock_init(&xhci->complete_list_lock);
- INIT_LIST_HEAD(&xhci->complete_list);
-
- spin_lock_init(&xhci->frame_list_lock);
-
- /* We need exactly one page (per XHCI specs), how convenient */
- /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
-#if PAGE_SIZE < (4 * 1024)
-#error PAGE_SIZE is not atleast 4k
-#endif
- bus = usb_alloc_bus(&xhci_device_operations);
- if (!bus) {
- err("unable to allocate bus");
- goto err_alloc_bus;
- }
-
- xhci->bus = bus;
- bus->bus_name = "XHCI";
- bus->hcpriv = xhci;
-
- usb_register_bus(xhci->bus);
-
- /* Initialize the root hub */
-
- xhci->rh.numports = 0;
-
- xhci->bus->root_hub = xhci->rh.dev = usb_alloc_dev(NULL, xhci->bus);
- if (!xhci->rh.dev) {
- err("unable to allocate root hub");
- goto err_alloc_root_hub;
- }
-
- xhci->state = 0;
-
- return 0;
-
-/*
- * error exits:
- */
-err_start_root_hub:
- free_irq(xhci->irq, xhci);
- xhci->irq = -1;
-
-err_alloc_root_hub:
- usb_free_bus(xhci->bus);
- xhci->bus = NULL;
-
-err_alloc_bus:
- free_xhci(xhci);
-
-err_alloc_xhci:
- return retval;
-}
-
-static void usbif_status_change(usbif_fe_interface_status_changed_t *status)
-{
- ctrl_msg_t cmsg;
- usbif_fe_interface_connect_t up;
- long rc;
- usbif_t *usbif;
-
- switch ( status->status )
- {
- case USBIF_INTERFACE_STATUS_DESTROYED:
- printk(KERN_WARNING "Unexpected usbif-DESTROYED message in state %d\n",
- xhci->state);
- break;
-
- case USBIF_INTERFACE_STATUS_DISCONNECTED:
- if ( xhci->state != USBIF_STATE_CLOSED )
- {
- printk(KERN_WARNING "Unexpected usbif-DISCONNECTED message"
- " in state %d\n", xhci->state);
- break;
- /* Not bothering to do recovery here for now. Keep things
- * simple. */
- }
-
- /* Move from CLOSED to DISCONNECTED state. */
- xhci->usbif = usbif = (usbif_t *)__get_free_page(GFP_KERNEL);
- usbif->req_prod = usbif->resp_prod = 0;
- xhci->state = USBIF_STATE_DISCONNECTED;
-
- /* Construct an interface-CONNECT message for the domain controller. */
- cmsg.type = CMSG_USBIF_FE;
- cmsg.subtype = CMSG_USBIF_FE_INTERFACE_CONNECT;
- cmsg.length = sizeof(usbif_fe_interface_connect_t);
- up.shmem_frame = virt_to_machine(usbif) >> PAGE_SHIFT;
- memcpy(cmsg.msg, &up, sizeof(up));
-
- /* Tell the controller to bring up the interface. */
- ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
- break;
-
- case USBIF_INTERFACE_STATUS_CONNECTED:
- if ( xhci->state == USBIF_STATE_CLOSED )
- {
- printk(KERN_WARNING "Unexpected usbif-CONNECTED message"
- " in state %d\n", xhci->state);
- break;
- }
-
- xhci->evtchn = status->evtchn;
- xhci->irq = bind_evtchn_to_irq(xhci->evtchn);
- xhci->bandwidth = status->bandwidth;
- xhci->rh.numports = status->num_ports;
-
- xhci->rh.ports = kmalloc (sizeof(xhci_port_t) * xhci->rh.numports, GFP_KERNEL);
- memset(xhci->rh.ports, 0, sizeof(xhci_port_t) * xhci->rh.numports);
-
- printk("rh.dev @ %p\n", xhci->rh.dev);
-
- usb_connect(xhci->rh.dev);
-
- if (usb_new_device(xhci->rh.dev) != 0) {
- err("unable to start root hub");
- }
-
- /* Allocate the appropriate USB bandwidth here... Need to
- * somehow know what the total available is thought to be so we
- * can calculate the reservation correctly. */
- usb_claim_bandwidth(xhci->rh.dev, xhci->rh.urb,
- 1000 - xhci->bandwidth, 0);
-
- if ( (rc = request_irq(xhci->irq, xhci_interrupt,
- SA_SAMPLE_RANDOM, "usbif", xhci)) )
- printk(KERN_ALERT"usbfront request_irq failed (%ld)\n",rc);
-
- printk(KERN_INFO __FILE__ ": USB XHCI: SHM at %p (0x%lx), EVTCHN %d IRQ %d\n",
- xhci->usbif, virt_to_machine(xhci->usbif), xhci->evtchn, xhci->irq);
-
- xhci->state = USBIF_STATE_CONNECTED;
-
- break;
-
- default:
- printk(KERN_WARNING "Status change to unknown value %d\n",
- status->status);
- break;
- }
-}
-
-
-static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
-{
- switch ( msg->subtype )
- {
- case CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED:
- if ( msg->length != sizeof(usbif_fe_interface_status_changed_t) )
- goto parse_error;
- usbif_status_change((usbif_fe_interface_status_changed_t *)
- &msg->msg[0]);
- break;
-
- /* New interface...? */
- default:
- goto parse_error;
- }
-
- ctrl_if_send_response(msg);
- return;
-
- parse_error:
- msg->length = 0;
- ctrl_if_send_response(msg);
-}
-
-
-static int __init xhci_hcd_init(void)
-{
- int retval = -ENOMEM, i;
- usbif_fe_interface_status_changed_t st;
- control_msg_t cmsg;
-
- if ( (xen_start_info.flags & SIF_INITDOMAIN)
- || (xen_start_info.flags & SIF_USB_BE_DOMAIN) )
- return 0;
-
- info(DRIVER_DESC " " DRIVER_VERSION);
-
- if (debug) {
- errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
- if (!errbuf)
- goto errbuf_failed;
- }
-
- xhci_up_cachep = kmem_cache_create("xhci_urb_priv",
- sizeof(struct urb_priv), 0, 0, NULL, NULL);
- if (!xhci_up_cachep)
- goto up_failed;
-
- /* Lazily avoid unloading issues for now. ;-)*/
- MOD_INC_USE_COUNT;
-
- /* Let the domain controller know we're here. For now we wait until
- * connection, as for the block and net drivers. This is only strictly
- * necessary if we're going to boot off a USB device. */
- printk(KERN_INFO "Initialising Xen virtual USB hub\n");
-
- (void)ctrl_if_register_receiver(CMSG_USBIF_FE, usbif_ctrlif_rx,
- CALLBACK_IN_BLOCKING_CONTEXT);
-
- alloc_xhci();
-
- /* Send a driver-UP notification to the domain controller. */
- cmsg.type = CMSG_USBIF_FE;
- cmsg.subtype = CMSG_USBIF_FE_DRIVER_STATUS_CHANGED;
- cmsg.length = sizeof(usbif_fe_driver_status_changed_t);
- st.status = USBIF_DRIVER_STATUS_UP;
- memcpy(cmsg.msg, &st, sizeof(st));
- ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
-
- /*
- * We should read 'nr_interfaces' from response message and wait
- * for notifications before proceeding. For now we assume that we
- * will be notified of exactly one interface.
- */
- for ( i=0; (xhci->state != USBIF_STATE_CONNECTED) && (i < 10*HZ); i++ )
- {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
- }
-
- if (xhci->state != USBIF_STATE_CONNECTED)
- printk(KERN_INFO "Timeout connecting USB frontend driver!\n");
-
- return 0;
-
-up_failed:
-
- if (errbuf)
- kfree(errbuf);
-
-errbuf_failed:
-
- return retval;
-}
-
-static void __exit xhci_hcd_cleanup(void)
-{
- if (kmem_cache_destroy(xhci_up_cachep))
- printk(KERN_INFO "xhci: not all urb_priv's were freed\n");
-
-// release_xhci(); do some calls here
-
-
- if (errbuf)
- kfree(errbuf);
-}
-
-module_init(xhci_hcd_init);
-module_exit(xhci_hcd_cleanup);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
+++ /dev/null
-#ifndef __LINUX_XHCI_H
-#define __LINUX_XHCI_H
-
-#include <linux/list.h>
-#include <linux/usb.h>
-#include "../usbif.h"
-#include <linux/spinlock.h>
-
-#define XHCI_NUMFRAMES 1024 /* in the frame list [array] */
-#define XHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
-#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
-
-/* In the absence of actual hardware state, we maintain the current known state
- * of the virtual hub ports in this data structure.
- */
-typedef struct
-{
- unsigned int cs :1; /* Connection status. do we really need this /and/ ccs? */
- unsigned int cs_chg :1; /* Connection status change. */
- unsigned int pe :1; /* Port enable. */
- unsigned int pe_chg :1; /* Port enable change. */
- unsigned int ccs :1; /* Current connect status. */
- unsigned int susp :1; /* Suspended. */
- unsigned int lsda :1; /* Low speed device attached. */
- unsigned int pr :1; /* Port reset. */
-
- /* Device info? */
-} xhci_port_t;
-
-struct xhci_frame_list {
- __u32 frame[XHCI_NUMFRAMES];
-
- void *frame_cpu[XHCI_NUMFRAMES];
-};
-
-struct urb_priv;
-
-#define xhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
-#define xhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
-
-#define xhci_maxlen(token) ((token) >> 21)
-#define xhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
-#define xhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
-#define xhci_endpoint(token) (((token) >> 15) & 0xf)
-#define xhci_devaddr(token) (((token) >> 8) & 0x7f)
-#define xhci_devep(token) (((token) >> 8) & 0x7ff)
-#define xhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
-#define xhci_packetout(token) (xhci_packetid(token) != USB_PID_IN)
-#define xhci_packetin(token) (xhci_packetid(token) == USB_PID_IN)
-
-struct virt_root_hub {
- struct usb_device *dev;
- int devnum; /* Address of Root Hub endpoint */
- struct urb *urb;
- void *int_addr;
- int send;
- int interval;
- int numports;
- int c_p_r[8];
- struct timer_list rh_int_timer;
- spinlock_t port_state_lock;
- xhci_port_t *ports; /* */
-};
-
-/*
- * This describes the full xhci information.
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs.
- */
-struct xhci {
-
-#ifdef CONFIG_PROC_FS
- /* procfs */
- int num;
- struct proc_dir_entry *proc_entry;
-#endif
-
- int evtchn; /* Interdom channel to backend */
- int irq; /* Bound to evtchn */
- int state; /* State of this USB interface */
- unsigned long bandwidth;
- int handle;
-
- struct usb_bus *bus;
-
- spinlock_t frame_list_lock;
- struct xhci_frame_list *fl; /* P: xhci->frame_list_lock */
- int is_suspended;
-
- /* Main list of URB's currently controlled by this HC */
- spinlock_t urb_list_lock;
- struct list_head urb_list; /* P: xhci->urb_list_lock */
-
- /* List of asynchronously unlinked URB's */
- spinlock_t urb_remove_list_lock;
- struct list_head urb_remove_list; /* P: xhci->urb_remove_list_lock */
-
- /* List of URB's awaiting completion callback */
- spinlock_t complete_list_lock;
- struct list_head complete_list; /* P: xhci->complete_list_lock */
-
- struct virt_root_hub rh; /* private data of the virtual root hub */
-
- spinlock_t response_lock;
-
- usbif_t *usbif;
- int usb_resp_cons;
-};
-
-struct urb_priv {
- struct urb *urb;
- usbif_iso_t *schedule;
- struct usb_device *dev;
-
- int in_progress : 1; /* QH was queued (not linked in) */
- int short_control_packet : 1; /* If we get a short packet during */
- /* a control transfer, retrigger */
- /* the status phase */
-
- int status; /* Final status */
-
- unsigned long inserttime; /* In jiffies */
-
- struct list_head queue_list; /* P: xhci->frame_list_lock */
- struct list_head complete_list; /* P: xhci->complete_list_lock */
-};
-
-/*
- * Locking in xhci.c
- *
- * spinlocks are used extensively to protect the many lists and data
- * structures we have. It's not that pretty, but it's necessary. We
- * need to be done with all of the locks (except complete_list_lock) when
- * we call urb->complete. I've tried to make it simple enough so I don't
- * have to spend hours racking my brain trying to figure out if the
- * locking is safe.
- *
- * Here's the safe locking order to prevent deadlocks:
- *
- * #1 xhci->urb_list_lock
- * #2 urb->lock
- * #3 xhci->urb_remove_list_lock, xhci->frame_list_lock,
- * xhci->qh_remove_list_lock
- * #4 xhci->complete_list_lock
- *
- * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
- * at the lowest level FIRST and NEVER grab locks at the same level at the
- * same time.
- *
- * So, if you need xhci->urb_list_lock, grab it before you grab urb->lock
- */
-
-/* -------------------------------------------------------------------------
- Virtual Root HUB
- ------------------------------------------------------------------------- */
-/* destination of request */
-#define RH_DEVICE 0x00
-#define RH_INTERFACE 0x01
-#define RH_ENDPOINT 0x02
-#define RH_OTHER 0x03
-
-#define RH_CLASS 0x20
-#define RH_VENDOR 0x40
-
-/* Requests: bRequest << 8 | bmRequestType */
-#define RH_GET_STATUS 0x0080
-#define RH_CLEAR_FEATURE 0x0100
-#define RH_SET_FEATURE 0x0300
-#define RH_SET_ADDRESS 0x0500
-#define RH_GET_DESCRIPTOR 0x0680
-#define RH_SET_DESCRIPTOR 0x0700
-#define RH_GET_CONFIGURATION 0x0880
-#define RH_SET_CONFIGURATION 0x0900
-#define RH_GET_STATE 0x0280
-#define RH_GET_INTERFACE 0x0A80
-#define RH_SET_INTERFACE 0x0B00
-#define RH_SYNC_FRAME 0x0C80
-/* Our Vendor Specific Request */
-#define RH_SET_EP 0x2000
-
-/* Hub port features */
-#define RH_PORT_CONNECTION 0x00
-#define RH_PORT_ENABLE 0x01
-#define RH_PORT_SUSPEND 0x02
-#define RH_PORT_OVER_CURRENT 0x03
-#define RH_PORT_RESET 0x04
-#define RH_PORT_POWER 0x08
-#define RH_PORT_LOW_SPEED 0x09
-#define RH_C_PORT_CONNECTION 0x10
-#define RH_C_PORT_ENABLE 0x11
-#define RH_C_PORT_SUSPEND 0x12
-#define RH_C_PORT_OVER_CURRENT 0x13
-#define RH_C_PORT_RESET 0x14
-
-/* Hub features */
-#define RH_C_HUB_LOCAL_POWER 0x00
-#define RH_C_HUB_OVER_CURRENT 0x01
-#define RH_DEVICE_REMOTE_WAKEUP 0x00
-#define RH_ENDPOINT_STALL 0x01
-
-/* Our Vendor Specific feature */
-#define RH_REMOVE_EP 0x00
-
-#define RH_ACK 0x01
-#define RH_REQ_ERR -1
-#define RH_NACK 0x00
-
-#endif
-
+++ /dev/null
-/******************************************************************************
- * usbif.h
- *
- * Unified block-device I/O interface for Xen guest OSes.
- *
- * Copyright (c) 2003-2004, Keir Fraser
- */
-
-#ifndef __SHARED_USBIF_H__
-#define __SHARED_USBIF_H__
-
-#define usbif_vdev_t u16
-#define usbif_sector_t u64
-
-#define USBIF_OP_IO 0
-#define USBIF_OP_PROBE 1 /* Is there a device on this port? */
-#define USBIF_OP_RESET 2 /* Reset a virtual USB port. */
-
-/* NB. Ring size must be small enough for sizeof(usbif_ring_t) <= PAGE_SIZE. */
-#define USBIF_RING_SIZE 64
-
-/* XXX this does not want to be here! it really ought to be dynamic but it can
- * live here for now */
-#define NUM_PORTS 1
-
-typedef struct {
- unsigned long id; /* 0: private guest value, echoed in resp */
- u8 operation; /* 4: USBIF_OP_??? */
- u8 __pad1;
- usbif_vdev_t port; /* 6 : guest virtual USB port */
- unsigned long devnum :7; /* 8 : Device address, as seen by the guest.*/
- unsigned long endpoint :4; /* Device endpoint. */
- unsigned long direction :1; /* Pipe direction. */
- unsigned long speed :1; /* Pipe speed. */
- unsigned long pipe_type :2; /* Pipe type (iso, bulk, int, ctrl) */
- unsigned long __pad2 :18;
- unsigned long transfer_buffer; /* 12: Machine address */
- unsigned long length; /* 16: Buffer length */
- unsigned long transfer_flags; /* 20: For now just pass Linux transfer
- * flags - this may change. */
- unsigned char setup[8]; /* 22 Embed setup packets directly. */
- unsigned long iso_schedule; /* 30 Machine address of transfer sched (iso
- * only) */
- unsigned long num_iso; /* 34 : length of iso schedule */
- unsigned long timeout; /* 38: timeout in ms */
-} PACKED usbif_request_t; /* 42 */
-/* Data we need to pass:
- * - Transparently handle short packets or complain at us?
- */
-
-typedef struct {
- unsigned long id; /* 0: copied from request */
- u8 operation; /* 4: copied from request */
- u8 data; /* 5: Small chunk of in-band data */
- s16 status; /* 6: USBIF_RSP_??? */
- unsigned long transfer_mutex; /* Used for cancelling requests atomically. */
- unsigned long length; /* 8: How much data we really got */
-} PACKED usbif_response_t;
-
-#define USBIF_RSP_ERROR -1 /* non-specific 'error' */
-#define USBIF_RSP_OKAY 0 /* non-specific 'okay' */
-
-/*
- * We use a special capitalised type name because it is _essential_ that all
- * arithmetic on indexes is done on an integer type of the correct size.
- */
-typedef u32 USBIF_RING_IDX;
-
-/*
- * Ring indexes are 'free running'. That is, they are not stored modulo the
- * size of the ring buffer. The following macro converts a free-running counter
- * into a value that can directly index a ring-buffer array.
- */
-#define MASK_USBIF_IDX(_i) ((_i)&(USBIF_RING_SIZE-1))
-
-typedef struct {
- USBIF_RING_IDX req_prod; /* 0: Request producer. Updated by front-end. */
- USBIF_RING_IDX resp_prod; /* 4: Response producer. Updated by back-end. */
-
- union { /* 8 */
- usbif_request_t req;
- usbif_response_t resp;
- } PACKED ring[USBIF_RING_SIZE];
-} PACKED usbif_t;
-
-
-
-/*
- * USBIF_OP_PROBE:
- * The request format for a probe request is constrained as follows:
- * @operation == USBIF_OP_PROBE
- * @nr_segments == size of probe buffer in pages
- * @device == unused (zero)
- * @id == any value (echoed in response message)
- * @sector_num == unused (zero)
- * @frame_and_sects == list of page-sized buffers.
- * (i.e., @first_sect == 0, @last_sect == 7).
- *
- * The response is a list of vdisk_t elements copied into the out-of-band
- * probe buffer. On success the response status field contains the number
- * of vdisk_t elements.
- */
-
-typedef struct {
- unsigned long length; /* IN = expected, OUT = actual */
- unsigned long buffer_offset; /* IN offset in buffer specified in main
- packet */
- unsigned long status; /* OUT Status for this packet. */
-} usbif_iso_t;
-
-#endif /* __SHARED_USBIF_H__ */
cd ${AD}/arch/xen/drivers/blkif/frontend
ln -sf ../../../../../${LINUX_26}/drivers/xen/blkfront/blkfront.c
-
+cd ${AD}/arch/xen/drivers/usbif/frontend
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbfront/usbfront.c main.c
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbfront/xhci.h
+
+cd ${AD}/arch/xen/drivers/usbif/backend
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbback/common.h
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbback/control.c
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbback/interface.c
+ln -sf ../../../../../${LINUX_26}/drivers/xen/usbback/usbback.c main.c
--- /dev/null
+
+#ifndef __USBIF__BACKEND__COMMON_H__
+#define __USBIF__BACKEND__COMMON_H__
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/rbtree.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/pgalloc.h>
+#include <asm-xen/ctrl_if.h>
+#include <asm-xen/hypervisor.h>
+
+#include <asm-xen/xen-public/io/usbif.h>
+
+#if 0
+#define ASSERT(_p) \
+ if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
+ __LINE__, __FILE__); *(int*)0=0; }
+#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
+ __FILE__ , __LINE__ , ## _a )
+#else
+#define ASSERT(_p) ((void)0)
+#define DPRINTK(_f, _a...) ((void)0)
+#endif
+
+typedef struct usbif_priv_st usbif_priv_t;
+
+struct usbif_priv_st {
+ /* Unique identifier for this interface. */
+ domid_t domid;
+ unsigned int handle;
+ /* Physical parameters of the comms window. */
+ unsigned long shmem_frame;
+ unsigned int evtchn;
+ int irq;
+ /* Comms information. */
+ usbif_t *usb_ring_base; /* ioremap()'ed ptr to shmem_frame. */
+ USBIF_RING_IDX usb_req_cons; /* Request consumer. */
+ USBIF_RING_IDX usb_resp_prod; /* Private version of resp. producer. */
+ /* Private fields. */
+ enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
+ /*
+ * DISCONNECT response is deferred until pending requests are ack'ed.
+ * We therefore need to store the id from the original request.
+ */
+ u8 disconnect_rspid;
+ usbif_priv_t *hash_next;
+ struct list_head usbif_list;
+ spinlock_t usb_ring_lock;
+ atomic_t refcnt;
+ atomic_t work_scheduled;
+
+ struct work_struct work;
+};
+
+void usbif_create(usbif_be_create_t *create);
+void usbif_destroy(usbif_be_destroy_t *destroy);
+void usbif_connect(usbif_be_connect_t *connect);
+int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id);
+void usbif_disconnect_complete(usbif_priv_t *up);
+
+void usbif_release_port(usbif_be_release_port_t *msg);
+int usbif_claim_port(usbif_be_claim_port_t *msg);
+void usbif_release_ports(usbif_priv_t *up);
+
+usbif_priv_t *usbif_find(domid_t domid);
+#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
+#define usbif_put(_b) \
+ do { \
+ if ( atomic_dec_and_test(&(_b)->refcnt) ) \
+ usbif_disconnect_complete(_b); \
+ } while (0)
+
+
+void usbif_interface_init(void);
+void usbif_ctrlif_init(void);
+
+void usbif_deschedule(usbif_priv_t *usbif);
+
+irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs);
+
+#endif /* __USBIF__BACKEND__COMMON_H__ */
--- /dev/null
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/control.c
+ *
+ * Routines for interfacing with the control plane.
+ *
+ * Copyright (c) 2004, Keir Fraser
+ */
+
+#include "common.h"
+
+static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
+{
+ DPRINTK("Received usbif backend message, subtype=%d\n", msg->subtype);
+
+ switch ( msg->subtype )
+ {
+ case CMSG_USBIF_BE_CREATE:
+ if ( msg->length != sizeof(usbif_be_create_t) )
+ goto parse_error;
+ usbif_create((usbif_be_create_t *)&msg->msg[0]);
+ break;
+ case CMSG_USBIF_BE_DESTROY:
+ if ( msg->length != sizeof(usbif_be_destroy_t) )
+ goto parse_error;
+ usbif_destroy((usbif_be_destroy_t *)&msg->msg[0]);
+ break;
+ case CMSG_USBIF_BE_CONNECT:
+ if ( msg->length != sizeof(usbif_be_connect_t) )
+ goto parse_error;
+ usbif_connect((usbif_be_connect_t *)&msg->msg[0]);
+ break;
+ case CMSG_USBIF_BE_DISCONNECT:
+ if ( msg->length != sizeof(usbif_be_disconnect_t) )
+ goto parse_error;
+ if ( !usbif_disconnect((usbif_be_disconnect_t *)&msg->msg[0],msg->id) )
+ return; /* Sending the response is deferred until later. */
+ break;
+ case CMSG_USBIF_BE_CLAIM_PORT:
+ if ( msg->length != sizeof(usbif_be_claim_port_t) )
+ goto parse_error;
+ usbif_claim_port((usbif_be_claim_port_t *)&msg->msg[0]);
+ break;
+ case CMSG_USBIF_BE_RELEASE_PORT:
+ if ( msg->length != sizeof(usbif_be_release_port_t) )
+ goto parse_error;
+ usbif_release_port((usbif_be_release_port_t *)&msg->msg[0]);
+ break;
+ default:
+ goto parse_error;
+ }
+
+ ctrl_if_send_response(msg);
+ return;
+
+ parse_error:
+ DPRINTK("Parse error while reading message subtype %d, len %d\n",
+ msg->subtype, msg->length);
+ msg->length = 0;
+ ctrl_if_send_response(msg);
+}
+
+void usbif_ctrlif_init(void)
+{
+ ctrl_msg_t cmsg;
+ usbif_be_driver_status_changed_t st;
+
+ (void)ctrl_if_register_receiver(CMSG_USBIF_BE, usbif_ctrlif_rx,
+ CALLBACK_IN_BLOCKING_CONTEXT);
+
+ /* Send a driver-UP notification to the domain controller. */
+ cmsg.type = CMSG_USBIF_BE;
+ cmsg.subtype = CMSG_USBIF_BE_DRIVER_STATUS_CHANGED;
+ cmsg.length = sizeof(usbif_be_driver_status_changed_t);
+ st.status = USBIF_DRIVER_STATUS_UP;
+ memcpy(cmsg.msg, &st, sizeof(st));
+ ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+}
--- /dev/null
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/interface.c
+ *
+ * USB device interface management.
+ *
+ * by Mark Williamson, Copyright (c) 2004
+ */
+
+
+/******************************************************************************
+ * arch/xen/drivers/blkif/backend/interface.c
+ *
+ * Block-device interface management.
+ *
+ * Copyright (c) 2004, Keir Fraser
+ */
+
+#include "common.h"
+
+#define USBIF_HASHSZ 1024
+#define USBIF_HASH(_d) (((int)(_d))&(USBIF_HASHSZ-1))
+
+static kmem_cache_t *usbif_priv_cachep;
+static usbif_priv_t *usbif_priv_hash[USBIF_HASHSZ];
+
+usbif_priv_t *usbif_find(domid_t domid)
+{
+ usbif_priv_t *up = usbif_priv_hash[USBIF_HASH(domid)];
+ while ( (up != NULL ) && ( up->domid != domid ) )
+ up = up->hash_next;
+ return up;
+}
+
+static void __usbif_disconnect_complete(void *arg)
+{
+ usbif_priv_t *usbif = (usbif_priv_t *)arg;
+ ctrl_msg_t cmsg;
+ usbif_be_disconnect_t disc;
+
+ /*
+ * These can't be done in usbif_disconnect() because at that point there
+ * may be outstanding requests at the device whose asynchronous responses
+ * must still be notified to the remote driver.
+ */
+ unbind_evtchn_from_irq(usbif->evtchn);
+ vfree(usbif->usb_ring_base);
+
+ /* Construct the deferred response message. */
+ cmsg.type = CMSG_USBIF_BE;
+ cmsg.subtype = CMSG_USBIF_BE_DISCONNECT;
+ cmsg.id = usbif->disconnect_rspid;
+ cmsg.length = sizeof(usbif_be_disconnect_t);
+ disc.domid = usbif->domid;
+ disc.status = USBIF_BE_STATUS_OKAY;
+ memcpy(cmsg.msg, &disc, sizeof(disc));
+
+ /*
+ * Make sure message is constructed /before/ status change, because
+ * after the status change the 'usbif' structure could be deallocated at
+ * any time. Also make sure we send the response /after/ status change,
+ * as otherwise a subsequent CONNECT request could spuriously fail if
+ * another CPU doesn't see the status change yet.
+ */
+ mb();
+ if ( usbif->status != DISCONNECTING )
+ BUG();
+ usbif->status = DISCONNECTED;
+ mb();
+
+ /* Send the successful response. */
+ ctrl_if_send_response(&cmsg);
+}
+
+void usbif_disconnect_complete(usbif_priv_t *up)
+{
+ INIT_WORK(&up->work, __usbif_disconnect_complete, (void *)up);
+ schedule_work(&up->work);
+}
+
+void usbif_create(usbif_be_create_t *create)
+{
+ domid_t domid = create->domid;
+ usbif_priv_t **pup, *up;
+
+ if ( (up = kmem_cache_alloc(usbif_priv_cachep, GFP_KERNEL)) == NULL )
+ {
+ DPRINTK("Could not create usbif: out of memory\n");
+ create->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+ return;
+ }
+
+ memset(up, 0, sizeof(*up));
+ up->domid = domid;
+ up->status = DISCONNECTED;
+ spin_lock_init(&up->usb_ring_lock);
+ atomic_set(&up->refcnt, 0);
+
+ pup = &usbif_priv_hash[USBIF_HASH(domid)];
+ while ( *pup != NULL )
+ {
+ if ( (*pup)->domid == domid )
+ {
+ create->status = USBIF_BE_STATUS_INTERFACE_EXISTS;
+ kmem_cache_free(usbif_priv_cachep, up);
+ return;
+ }
+ pup = &(*pup)->hash_next;
+ }
+
+ up->hash_next = *pup;
+ *pup = up;
+
+ create->status = USBIF_BE_STATUS_OKAY;
+}
+
+void usbif_destroy(usbif_be_destroy_t *destroy)
+{
+ domid_t domid = destroy->domid;
+ usbif_priv_t **pup, *up;
+
+ pup = &usbif_priv_hash[USBIF_HASH(domid)];
+ while ( (up = *pup) != NULL )
+ {
+ if ( up->domid == domid )
+ {
+ if ( up->status != DISCONNECTED )
+ goto still_connected;
+ goto destroy;
+ }
+ pup = &up->hash_next;
+ }
+
+ destroy->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+ return;
+
+ still_connected:
+ destroy->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
+ return;
+
+ destroy:
+ *pup = up->hash_next;
+ usbif_release_ports(up);
+ kmem_cache_free(usbif_priv_cachep, up);
+ destroy->status = USBIF_BE_STATUS_OKAY;
+}
+
+void usbif_connect(usbif_be_connect_t *connect)
+{
+ domid_t domid = connect->domid;
+ unsigned int evtchn = connect->evtchn;
+ unsigned long shmem_frame = connect->shmem_frame;
+ struct vm_struct *vma;
+ pgprot_t prot;
+ int error;
+ usbif_priv_t *up;
+
+ up = usbif_find(domid);
+ if ( unlikely(up == NULL) )
+ {
+ DPRINTK("usbif_connect attempted for non-existent usbif (%u)\n",
+ connect->domid);
+ connect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+ return;
+ }
+
+ if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
+ {
+ connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+ return;
+ }
+
+ prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED);
+ error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
+ shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
+ prot, domid);
+ if ( error != 0 )
+ {
+ if ( error == -ENOMEM )
+ connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
+ else if ( error == -EFAULT )
+ connect->status = USBIF_BE_STATUS_MAPPING_ERROR;
+ else
+ connect->status = USBIF_BE_STATUS_ERROR;
+ vfree(vma->addr);
+ return;
+ }
+
+ if ( up->status != DISCONNECTED )
+ {
+ connect->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
+ vfree(vma->addr);
+ return;
+ }
+
+ up->evtchn = evtchn;
+ up->irq = bind_evtchn_to_irq(evtchn);
+ up->shmem_frame = shmem_frame;
+ up->usb_ring_base = (usbif_t *)vma->addr;
+ up->status = CONNECTED;
+ usbif_get(up);
+
+ request_irq(up->irq, usbif_be_int, 0, "usbif-backend", up);
+
+ connect->status = USBIF_BE_STATUS_OKAY;
+}
+
+/* Remove URBs for this interface before destroying it. */
+void usbif_deschedule(usbif_priv_t *up)
+{
+ remove_from_usbif_list(up);
+}
+
+int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id)
+{
+ domid_t domid = disconnect->domid;
+ usbif_priv_t *up;
+
+ up = usbif_find(domid);
+ if ( unlikely(up == NULL) )
+ {
+ DPRINTK("usbif_disconnect attempted for non-existent usbif"
+ " (%u)\n", disconnect->domid);
+ disconnect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
+ return 1; /* Caller will send response error message. */
+ }
+
+ if ( up->status == CONNECTED )
+ {
+ up->status = DISCONNECTING;
+ up->disconnect_rspid = rsp_id;
+ wmb(); /* Let other CPUs see the status change. */
+ free_irq(up->irq, up);
+ usbif_deschedule(up);
+ usbif_put(up);
+ return 0; /* Caller should not send response message. */
+ }
+
+ disconnect->status = USBIF_BE_STATUS_OKAY;
+ return 1;
+}
+
+void __init usbif_interface_init(void)
+{
+ usbif_priv_cachep = kmem_cache_create("usbif_priv_cache",
+ sizeof(usbif_priv_t),
+ 0, 0, NULL, NULL);
+ memset(usbif_priv_hash, 0, sizeof(usbif_priv_hash));
+}
--- /dev/null
+/******************************************************************************
+ * arch/xen/drivers/usbif/backend/main.c
+ *
+ * Backend for the Xen virtual USB driver - provides an abstraction of a
+ * USB host controller to the corresponding frontend driver.
+ *
+ * by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge
+ *
+ * Based on arch/xen/drivers/blkif/backend/main.c
+ * Copyright (c) 2003-2004, Keir Fraser & Steve Hand
+ */
+
+#include "common.h"
+
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/tqueue.h>
+
+/*
+ * This is rather arbitrary.
+ */
+#define MAX_PENDING_REQS 4
+#define BATCH_PER_DOMAIN 1
+
+static unsigned long mmap_vstart;
+
+/* Needs to be sufficiently large that we can map the (large) buffers
+ * the USB mass storage driver wants. */
+#define MMAP_PAGES_PER_REQUEST \
+ (128)
+#define MMAP_PAGES \
+ (MAX_PENDING_REQS * MMAP_PAGES_PER_REQUEST)
+
+#define MMAP_VADDR(_req,_seg) \
+ (mmap_vstart + \
+ ((_req) * MMAP_PAGES_PER_REQUEST * PAGE_SIZE) + \
+ ((_seg) * PAGE_SIZE))
+
+#define MIN(x,y) ( ( x < y ) ? x : y )
+
+static spinlock_t owned_ports_lock;
+LIST_HEAD(owned_ports);
+
+/* A list of these structures is used to track ownership of physical USB
+ * ports. */
+typedef struct
+{
+ usbif_priv_t *usbif_priv;
+ char path[16];
+ int guest_port;
+ int enabled;
+ struct list_head list;
+ unsigned long guest_address; /* The USB device address that has been
+ * assigned by the guest. */
+ int dev_present; /* Is there a device present? */
+ struct usb_device * dev;
+ unsigned long ifaces; /* What interfaces are present on this device? */
+} owned_port_t;
+
+
+/*
+ * Each outstanding request that we've passed to the lower device layers has a
+ * 'pending_req' allocated to it. The request is complete, the specified
+ * domain has a response queued for it, with the saved 'id' passed back.
+ */
+typedef struct {
+ usbif_priv_t *usbif_priv;
+ usbif_iso_t *iso_sched;
+ unsigned long id;
+ int nr_pages;
+ unsigned short operation;
+ int status;
+} pending_req_t;
+
+/*
+ * We can't allocate pending_req's in order, since they may complete out of
+ * order. We therefore maintain an allocation ring. This ring also indicates
+ * when enough work has been passed down -- at that point the allocation ring
+ * will be empty.
+ */
+static pending_req_t pending_reqs[MAX_PENDING_REQS];
+static unsigned char pending_ring[MAX_PENDING_REQS];
+static spinlock_t pend_prod_lock = SPIN_LOCK_UNLOCKED;
+
+/* NB. We use a different index type to differentiate from shared blk rings. */
+typedef unsigned int PEND_RING_IDX;
+#define MASK_PEND_IDX(_i) ((_i)&(MAX_PENDING_REQS-1))
+static PEND_RING_IDX pending_prod, pending_cons;
+#define NR_PENDING_REQS (MAX_PENDING_REQS - pending_prod + pending_cons)
+
+static int do_usb_io_op(usbif_priv_t *usbif, int max_to_do);
+static void make_response(usbif_priv_t *usbif, unsigned long id,
+ unsigned short op, int st, int inband,
+ unsigned long actual_length);
+static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long port);
+static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req);
+static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid);
+static owned_port_t *usbif_find_port(char *);
+
+
+void dump_port(owned_port_t *p)
+{
+ printk("owned_port_t @ %p\n", p);
+ printk(" usbif_priv @ %p\n", p->usbif_priv);
+ printk(" path: %s\n", p->path);
+ printk(" guest_port: %d\n", p->guest_port);
+ printk(" guest_address: %ld\n", p->guest_address);
+ printk(" dev_present: %d\n", p->dev_present);
+ printk(" dev @ %p\n", p->dev);
+ printk(" ifaces: 0x%lx\n", p->ifaces);
+}
+
+
+
+static void fast_flush_area(int idx, int nr_pages)
+{
+ multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
+ int i;
+
+ for ( i = 0; i < nr_pages; i++ )
+ {
+ mcl[i].op = __HYPERVISOR_update_va_mapping;
+ mcl[i].args[0] = MMAP_VADDR(idx, i) >> PAGE_SHIFT;
+ mcl[i].args[1] = 0;
+ mcl[i].args[2] = 0;
+ }
+
+ mcl[nr_pages-1].args[2] = UVMF_FLUSH_TLB;
+ if ( unlikely(HYPERVISOR_multicall(mcl, nr_pages) != 0) )
+ BUG();
+}
+
+
+/******************************************************************
+ * USB INTERFACE SCHEDULER LIST MAINTENANCE
+ */
+
+static struct list_head usbio_schedule_list;
+static spinlock_t usbio_schedule_list_lock;
+
+static int __on_usbif_list(usbif_priv_t *up)
+{
+ return up->usbif_list.next != NULL;
+}
+
+void remove_from_usbif_list(usbif_priv_t *up)
+{
+ unsigned long flags;
+ if ( !__on_usbif_list(up) ) return;
+ spin_lock_irqsave(&usbio_schedule_list_lock, flags);
+ if ( __on_usbif_list(up) )
+ {
+ list_del(&up->usbif_list);
+ up->usbif_list.next = NULL;
+ usbif_put(up);
+ }
+ spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
+}
+
+static void add_to_usbif_list_tail(usbif_priv_t *up)
+{
+ unsigned long flags;
+ if ( __on_usbif_list(up) ) return;
+ spin_lock_irqsave(&usbio_schedule_list_lock, flags);
+ if ( !__on_usbif_list(up) && (up->status == CONNECTED) )
+ {
+ list_add_tail(&up->usbif_list, &usbio_schedule_list);
+ usbif_get(up);
+ }
+ spin_unlock_irqrestore(&usbio_schedule_list_lock, flags);
+}
+
+
+/******************************************************************
+ * COMPLETION CALLBACK -- Called as urb->complete()
+ */
+
+static void maybe_trigger_usbio_schedule(void);
+
+static void __end_usb_io_op(struct urb *purb)
+{
+ unsigned long flags;
+ pending_req_t *pending_req;
+ int pending_idx;
+
+ pending_req = purb->context;
+
+/* printk("Completed for id = %p to 0x%lx - 0x%lx\n", pending_req->id, */
+/* virt_to_machine(purb->transfer_buffer), */
+/* virt_to_machine(purb->transfer_buffer) */
+/* + pending_req->nr_pages * PAGE_SIZE); */
+
+ pending_idx = pending_req - pending_reqs;
+
+ ASSERT(purb->actual_length <= purb->transfer_buffer_length);
+ ASSERT(purb->actual_length <= pending_req->nr_pages * PAGE_SIZE);
+
+ /* An error fails the entire request. */
+ if ( purb->status )
+ {
+ printk("URB @ %p failed. Status %d\n", purb, purb->status);
+ }
+
+ if ( usb_pipetype(purb->pipe) == 0 )
+ {
+ int i;
+ usbif_iso_t *sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, pending_req->nr_pages - 1);
+
+ ASSERT(sched == pending_req->sched);
+
+ // printk("writing back schedule at %p\n", sched);
+
+ /* If we're dealing with an iso pipe, we need to copy back the schedule. */
+ for ( i = 0; i < purb->number_of_packets; i++ )
+ {
+ sched[i].length = purb->iso_frame_desc[i].actual_length;
+ ASSERT(sched[i].buffer_offset ==
+ purb->iso_frame_desc[i].offset);
+ sched[i].status = purb->iso_frame_desc[i].status;
+ }
+ }
+
+ // printk("Flushing %d pages\n", pending_req->nr_pages);
+ fast_flush_area(pending_req - pending_reqs, pending_req->nr_pages);
+
+ kfree(purb->setup_packet);
+
+ spin_lock_irqsave(&pending_req->usbif_priv->usb_ring_lock, flags);
+ make_response(pending_req->usbif_priv, pending_req->id,
+ pending_req->operation, pending_req->status, 0, purb->actual_length);
+ spin_unlock_irqrestore(&pending_req->usbif_priv->usb_ring_lock, flags);
+ usbif_put(pending_req->usbif_priv);
+
+ usb_free_urb(purb);
+
+ /* Free the pending request. */
+ spin_lock_irqsave(&pend_prod_lock, flags);
+ pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
+ spin_unlock_irqrestore(&pend_prod_lock, flags);
+
+ rmb();
+
+ /* Check for anything still waiting in the rings, having freed a request... */
+ maybe_trigger_usbio_schedule();
+}
+
+/******************************************************************
+ * SCHEDULER FUNCTIONS
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(usbio_schedule_wait);
+
+static int usbio_schedule(void *arg)
+{
+ DECLARE_WAITQUEUE(wq, current);
+
+ usbif_priv_t *up;
+ struct list_head *ent;
+
+ daemonize();
+
+ for ( ; ; )
+ {
+ /* Wait for work to do. */
+ add_wait_queue(&usbio_schedule_wait, &wq);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ( (NR_PENDING_REQS == MAX_PENDING_REQS) ||
+ list_empty(&usbio_schedule_list) )
+ schedule();
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&usbio_schedule_wait, &wq);
+
+ /* Queue up a batch of requests. */
+ while ( (NR_PENDING_REQS < MAX_PENDING_REQS) &&
+ !list_empty(&usbio_schedule_list) )
+ {
+ ent = usbio_schedule_list.next;
+ up = list_entry(ent, usbif_priv_t, usbif_list);
+ usbif_get(up);
+ remove_from_usbif_list(up);
+ if ( do_usb_io_op(up, BATCH_PER_DOMAIN) )
+ add_to_usbif_list_tail(up);
+ usbif_put(up);
+ }
+ }
+}
+
+static void maybe_trigger_usbio_schedule(void)
+{
+ /*
+ * Needed so that two processes, who together make the following predicate
+ * true, don't both read stale values and evaluate the predicate
+ * incorrectly. Incredibly unlikely to stall the scheduler on x86, but...
+ */
+ smp_mb();
+
+ if ( !list_empty(&usbio_schedule_list) )
+ wake_up(&usbio_schedule_wait);
+}
+
+
+/******************************************************************************
+ * NOTIFICATION FROM GUEST OS.
+ */
+
+irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ usbif_priv_t *up = dev_id;
+
+ smp_mb();
+
+ add_to_usbif_list_tail(up);
+
+ /* Will in fact /always/ trigger an io schedule in this case. */
+ maybe_trigger_usbio_schedule();
+
+ return IRQ_HANDLED;
+}
+
+
+
+/******************************************************************
+ * DOWNWARD CALLS -- These interface with the usb-device layer proper.
+ */
+
+static int do_usb_io_op(usbif_priv_t *up, int max_to_do)
+{
+ usbif_t *usb_ring = up->usb_ring_base;
+ usbif_request_t *req;
+ USBIF_RING_IDX i, rp;
+ int more_to_do = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->usb_ring_lock, flags);
+
+ rp = usb_ring->req_prod;
+ rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ /* Take items off the comms ring, taking care not to overflow. */
+ for ( i = up->usb_req_cons;
+ (i != rp) && ((i-up->usb_resp_prod) != USBIF_RING_SIZE);
+ i++ )
+ {
+ if ( (max_to_do-- == 0) || (NR_PENDING_REQS == MAX_PENDING_REQS) )
+ {
+ more_to_do = 1;
+ break;
+ }
+
+ req = &usb_ring->ring[MASK_USBIF_IDX(i)].req;
+
+ switch ( req->operation )
+ {
+ case USBIF_OP_PROBE:
+ dispatch_usb_probe(up, req->id, req->port);
+ break;
+
+ case USBIF_OP_IO:
+ /* Assemble an appropriate URB. */
+ dispatch_usb_io(up, req);
+ break;
+
+ case USBIF_OP_RESET:
+ dispatch_usb_reset(up, req->port);
+ break;
+
+ default:
+ DPRINTK("error: unknown USB io operation [%d]\n",
+ req->operation);
+ make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+ break;
+ }
+ }
+
+ up->usb_req_cons = i;
+
+ spin_unlock_irqrestore(&up->usb_ring_lock, flags);
+
+ return more_to_do;
+}
+
+static owned_port_t *find_guest_port(usbif_priv_t *up, int port)
+{
+ unsigned long flags;
+ struct list_head *l;
+
+ spin_lock_irqsave(&owned_ports_lock, flags);
+ list_for_each(l, &owned_ports)
+ {
+ owned_port_t *p = list_entry(l, owned_port_t, list);
+ if(p->usbif_priv == up && p->guest_port == port)
+ {
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+ return p;
+ }
+ }
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+ return NULL;
+}
+
+static void dispatch_usb_reset(usbif_priv_t *up, unsigned long portid)
+{
+ owned_port_t *port = find_guest_port(up, portid);
+ int ret = 0;
+
+
+ /* Allowing the guest to actually reset the device causes more problems
+ * than it's worth. We just fake it out in software but we will do a real
+ * reset when the interface is destroyed. */
+
+#if 0
+ printk("Reset port %d\n", portid);
+
+ dump_port(port);
+#endif
+
+ port->guest_address = 0;
+ /* If there's an attached device then the port is now enabled. */
+ if ( port->dev_present )
+ port->enabled = 1;
+ else
+ port->enabled = 0;
+
+ make_response(up, 0, USBIF_OP_RESET, ret, 0, 0);
+}
+
+static void dispatch_usb_probe(usbif_priv_t *up, unsigned long id, unsigned long portid)
+{
+ owned_port_t *port = find_guest_port(up, portid);
+ int ret;
+
+ if ( port != NULL )
+ ret = port->dev_present;
+ else
+ {
+ ret = -EINVAL;
+ printk("dispatch_usb_probe(): invalid port probe request (port %ld)\n",
+ portid);
+ }
+
+ /* Probe result is sent back in-band. Probes don't have an associated id
+ * right now... */
+ make_response(up, id, USBIF_OP_PROBE, ret, portid, 0);
+}
+
+owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req);
+
+static void dump_request(usbif_request_t *req)
+{
+ printk("id = 0x%lx\n", req->id);
+
+ printk("devnum %d\n", req->devnum);
+ printk("endpoint 0x%x\n", req->endpoint);
+ printk("direction %d\n", req->direction);
+ printk("speed %d\n", req->speed);
+ printk("pipe_type 0x%x\n", req->pipe_type);
+ printk("transfer_buffer 0x%lx\n", req->transfer_buffer);
+ printk("length 0x%lx\n", req->length);
+ printk("transfer_flags 0x%lx\n", req->transfer_flags);
+ printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ req->setup[0], req->setup[1], req->setup[2], req->setup[3],
+ req->setup[4], req->setup[5], req->setup[6], req->setup[7]);
+ printk("iso_schedule = 0x%lx\n", req->iso_schedule);
+ printk("num_iso %ld\n", req->num_iso);
+}
+
+void dump_urb(struct urb *urb)
+{
+ printk("dumping urb @ %p\n", urb);
+
+#define DUMP_URB_FIELD(name, format) printk(" " # name " " format "\n", urb-> name)
+
+ DUMP_URB_FIELD(pipe, "0x%x");
+ DUMP_URB_FIELD(status, "%d");
+ DUMP_URB_FIELD(transfer_flags, "0x%x");
+ DUMP_URB_FIELD(transfer_buffer, "%p");
+ DUMP_URB_FIELD(transfer_buffer_length, "%d");
+ DUMP_URB_FIELD(actual_length, "%d");
+}
+
+
+static void dispatch_usb_io(usbif_priv_t *up, usbif_request_t *req)
+{
+ unsigned long buffer_mach;
+ int i = 0, offset = 0,
+ pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
+ pending_req_t *pending_req;
+ unsigned long remap_prot;
+ multicall_entry_t mcl[MMAP_PAGES_PER_REQUEST];
+ struct urb *purb = NULL;
+ owned_port_t *port;
+ unsigned char *setup;
+
+// dump_request(req);
+
+ if ( NR_PENDING_REQS == MAX_PENDING_REQS )
+ {
+ printk("usbback: Max requests already queued. Now giving up!\n");
+
+ return;
+ }
+
+ port = find_port_for_request(up, req);
+
+ if(port == NULL)
+ {
+ printk("No such device! (%d)\n", req->devnum);
+ dump_request(req);
+
+ make_response(up, req->id, req->operation, -ENODEV, 0, 0);
+ return;
+ }
+
+ setup = kmalloc(8, GFP_ATOMIC | GFP_NOIO);
+
+ if ( setup == NULL )
+ goto no_mem;
+
+ /* Copy request out for safety. */
+ memcpy(setup, req->setup, 8);
+
+ if( setup[0] == 0x0 && setup[1] == 0x5)
+ {
+ /* To virtualise the USB address space, we need to intercept
+ * set_address messages and emulate. From the USB specification:
+ * bmRequestType = 0x0;
+ * Brequest = SET_ADDRESS (i.e. 0x5)
+ * wValue = device address
+ * wIndex = 0
+ * wLength = 0
+ * data = None
+ */
+ /* Store into the guest transfer buffer using cpu_to_le16 */
+ port->guest_address = le16_to_cpu(*(u16 *)(setup + 2));
+ /* Make a successful response. That was easy! */
+
+ make_response(up, req->id, req->operation, 0, 0, 0);
+
+ kfree(setup);
+ return;
+ }
+ else if ( setup[0] == 0x0 && setup[1] == 0x9 )
+ {
+ /* The host kernel needs to know what device configuration is in use
+ * because various error checks get confused otherwise. We just do
+ * configuration settings here, under controlled conditions.
+ */
+ usb_set_configuration(port->dev, setup[2]);
+
+ make_response(up, req->id, req->operation, 0, 0, 0);
+
+ kfree(setup);
+ return;
+ }
+
+ else if ( setup[0] == 0x1 && setup[1] == 0xB )
+ {
+ /* The host kernel needs to know what device interface is in use
+ * because various error checks get confused otherwise. We just do
+ * configuration settings here, under controlled conditions.
+ */
+ usb_set_interface(port->dev, (setup[4] | setup[5] << 8),
+ (setup[2] | setup[3] << 8) );
+
+ make_response(up, req->id, req->operation, 0, 0, 0);
+
+ kfree(setup);
+ return;
+ }
+
+ if ( ( req->transfer_buffer - (req->transfer_buffer & PAGE_MASK)
+ + req->length )
+ > MMAP_PAGES_PER_REQUEST * PAGE_SIZE )
+ {
+ printk("usbback: request of %d bytes too large, failing it\n", req->length);
+ make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+ kfree(setup);
+ return;
+ }
+
+ buffer_mach = req->transfer_buffer;
+
+ if( buffer_mach == 0 )
+ goto no_remap;
+
+ ASSERT((req->length >> PAGE_SHIFT) <= MMAP_PAGES_PER_REQUEST);
+ ASSERT(buffer_mach);
+
+ /* Always map writeable for now. */
+ remap_prot = _PAGE_PRESENT|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW;
+
+ for ( i = 0, offset = 0; offset < req->length;
+ i++, offset += PAGE_SIZE )
+ {
+ // printk("length = %d, offset = %d, looping!\n", req->length, offset);
+
+ mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
+ mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
+ mcl[i].args[1] = ((buffer_mach & PAGE_MASK) + offset) | remap_prot;
+ mcl[i].args[2] = 0;
+ mcl[i].args[3] = up->domid;
+
+ phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
+ FOREIGN_FRAME((buffer_mach + offset) >> PAGE_SHIFT);
+ // printk("i = %d\n", i);
+
+ ASSERT(virt_to_machine(MMAP_VADDR(pending_idx, i))
+ == buffer_mach + i << PAGE_SHIFT);
+ }
+
+ if ( req->pipe_type == 0 && req->num_iso > 0 ) /* Maybe schedule ISO... */
+ {
+ // printk("for iso, i = %d\n", i);
+ /* Map in ISO schedule, if necessary. */
+ mcl[i].op = __HYPERVISOR_update_va_mapping_otherdomain;
+ mcl[i].args[0] = MMAP_VADDR(pending_idx, i) >> PAGE_SHIFT;
+ mcl[i].args[1] = (req->iso_schedule & PAGE_MASK) | remap_prot;
+ mcl[i].args[2] = 0;
+ mcl[i].args[3] = up->domid;
+
+ phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
+ FOREIGN_FRAME(req->iso_schedule >> PAGE_SHIFT);
+
+ // printk("Mapped iso at %p\n", MMAP_VADDR(pending_idx, i));
+ i++;
+ }
+
+ // printk("Well we got this far!\n");
+
+ if ( unlikely(HYPERVISOR_multicall(mcl, i) != 0) )
+ BUG();
+
+ {
+ int j;
+ for ( j = 0; j < i; j++ )
+ {
+ if ( unlikely(mcl[j].args[5] != 0) )
+ {
+ printk("invalid buffer %d -- could not remap it\n", j);
+ fast_flush_area(pending_idx, i);
+ printk("sending invalid descriptor\n");
+ goto bad_descriptor;
+ }
+ }
+ }
+
+ no_remap:
+
+ ASSERT(i <= MMAP_PAGES_PER_REQUEST);
+ ASSERT(i * PAGE_SIZE >= req->length);
+
+ /* We have to do this because some things might complete out of order. */
+ pending_req = &pending_reqs[pending_idx];
+ pending_req->usbif_priv= up;
+ pending_req->id = req->id;
+ pending_req->operation = req->operation;
+ pending_req->nr_pages = i;
+
+
+
+ pending_cons++;
+
+ usbif_get(up);
+
+ /* Fill out an actual request for the USB layer. */
+ purb = usb_alloc_urb(req->num_iso);
+
+ if ( purb == NULL )
+ goto no_mem;
+
+ purb->dev = port->dev;
+ purb->context = pending_req;
+ purb->transfer_buffer = (void *)MMAP_VADDR(pending_idx, 0) + (buffer_mach & ~PAGE_MASK);
+ if(buffer_mach == 0)
+ purb->transfer_buffer = NULL;
+ purb->complete = __end_usb_io_op;
+ purb->transfer_buffer_length = req->length;
+ purb->transfer_flags = req->transfer_flags;
+
+/* if ( req->transfer_flags != 0 ) */
+/* dump_request(req); */
+
+ purb->pipe = 0;
+ purb->pipe |= req->direction << 7;
+ purb->pipe |= port->dev->devnum << 8;
+ purb->pipe |= req->speed << 26;
+ purb->pipe |= req->pipe_type << 30;
+ purb->pipe |= req->endpoint << 15;
+
+ purb->number_of_packets = req->num_iso;
+
+ /* Make sure there's always some kind of timeout. */
+ purb->timeout = ( req->timeout > 0 ) ? (req->timeout * HZ) / 1000
+ : 1000;
+
+ purb->setup_packet = setup;
+
+ if ( req->pipe_type == 0 ) /* ISO */
+ {
+ int j;
+ usbif_iso_t *iso_sched = (usbif_iso_t *)MMAP_VADDR(pending_idx, i - 1);
+
+ // printk("Reading iso sched at %p\n", iso_sched);
+
+ /* If we're dealing with an iso pipe, we need to copy in a schedule. */
+ for ( j = 0; j < req->num_iso; j++ )
+ {
+ purb->iso_frame_desc[j].length = iso_sched[j].length;
+ purb->iso_frame_desc[j].offset = iso_sched[j].buffer_offset;
+ iso_sched[j].status = 0;
+ }
+ pending_req->iso_sched = iso_sched;
+ }
+
+ {
+ int ret;
+ ret = usb_submit_urb(purb);
+
+ // dump_urb(purb);
+
+ if ( ret != 0 )
+ goto bad_descriptor; /* XXX free pending here! */
+ }
+
+ return;
+
+ bad_descriptor:
+ kfree ( setup );
+ if ( purb != NULL )
+ usb_free_urb(purb);
+ make_response(up, req->id, req->operation, -EINVAL, 0, 0);
+ return;
+
+ no_mem:
+ if ( setup != NULL )
+ kfree(setup);
+ make_response(up, req->id, req->operation, -ENOMEM, 0, 0);
+ return;
+}
+
+
+
+/******************************************************************
+ * MISCELLANEOUS SETUP / TEARDOWN / DEBUGGING
+ */
+
+
+static void make_response(usbif_priv_t *up, unsigned long id,
+ unsigned short op, int st, int inband,
+ unsigned long length)
+{
+ usbif_response_t *resp;
+ unsigned long flags;
+
+#if 0
+ printk("usbback: Sending response:\n");
+ printk(" id = 0x%x\n", id);
+ printk(" op = %d\n", op);
+ printk(" status = %d\n", st);
+ printk(" data = %d\n", inband);
+ printk(" length = %d\n", length);
+#endif
+
+ /* Place on the response ring for the relevant domain. */
+ spin_lock_irqsave(&up->usb_ring_lock, flags);
+ resp = &up->usb_ring_base->
+ ring[MASK_USBIF_IDX(up->usb_resp_prod)].resp;
+ resp->id = id;
+ resp->operation = op;
+ resp->status = st;
+ resp->data = inband;
+ resp->length = length;
+ wmb(); /* Ensure other side can see the response fields. */
+ up->usb_ring_base->resp_prod = ++up->usb_resp_prod;
+ spin_unlock_irqrestore(&up->usb_ring_lock, flags);
+
+ /* Kick the relevant domain. */
+ notify_via_evtchn(up->evtchn);
+}
+
+/**
+ * usbif_claim_port - claim devices on a port on behalf of guest
+ *
+ * Once completed, this will ensure that any device attached to that
+ * port is claimed by this driver for use by the guest.
+ */
+int usbif_claim_port(usbif_be_claim_port_t *msg)
+{
+ owned_port_t *o_p;
+
+ /* Sanity... */
+ if ( usbif_find_port(msg->path) != NULL )
+ {
+ printk("usbback: Attempted to claim USB port "
+ "we already own!\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&owned_ports_lock);
+
+ /* No need for a slab cache - this should be infrequent. */
+ o_p = kmalloc(sizeof(owned_port_t), GFP_KERNEL);
+
+ o_p->enabled = 0;
+ o_p->usbif_priv = usbif_find(msg->domid);
+ o_p->guest_port = msg->usbif_port;
+ o_p->dev_present = 0;
+ o_p->guest_address = 0; /* Default address. */
+
+ strcpy(o_p->path, msg->path);
+
+ list_add(&o_p->list, &owned_ports);
+
+ printk("usbback: Claimed USB port (%s) for %d.%d\n", o_p->path,
+ msg->domid, msg->usbif_port);
+
+ spin_unlock_irq(&owned_ports_lock);
+
+ /* Force a reprobe for unclaimed devices. */
+ usb_scan_devices();
+
+ return 0;
+}
+
+owned_port_t *find_port_for_request(usbif_priv_t *up, usbif_request_t *req)
+{
+ unsigned long flags;
+ struct list_head *port;
+
+ /* I'm assuming this is not called from IRQ context - correct? I think
+ * it's probably only called in response to control messages or plug events
+ * in the USB hub kernel thread, so should be OK. */
+ spin_lock_irqsave(&owned_ports_lock, flags);
+ list_for_each(port, &owned_ports)
+ {
+ owned_port_t *p = list_entry(port, owned_port_t, list);
+ if(p->usbif_priv == up && p->guest_address == req->devnum && p->enabled )
+ {
+#if 0
+ printk("Found port for devnum %d\n", req->devnum);
+
+ dump_port(p);
+#endif
+ return p;
+ }
+ }
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+ return NULL;
+}
+
+owned_port_t *usbif_find_port(char *path)
+{
+ struct list_head *port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&owned_ports_lock, flags);
+ list_for_each(port, &owned_ports)
+ {
+ owned_port_t *p = list_entry(port, owned_port_t, list);
+ if(!strcmp(path, p->path))
+ {
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+ return p;
+ }
+ }
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+
+ return NULL;
+}
+
+
+static void *probe(struct usb_device *dev, unsigned iface,
+ const struct usb_device_id *id)
+{
+ owned_port_t *p;
+
+ /* We don't care what the device is - if we own the port, we want it. We
+ * don't deal with device-specifics in this driver, so we don't care what
+ * the device actually is ;-) */
+ if ( ( p = usbif_find_port(dev->devpath) ) != NULL )
+ {
+ printk("usbback: claimed device attached to owned port\n");
+
+ p->dev_present = 1;
+ p->dev = dev;
+ set_bit(iface, &p->ifaces);
+
+ return p->usbif_priv;
+ }
+ else
+ printk("usbback: hotplug for non-owned port (%s), ignoring\n", dev->devpath);
+
+
+ return NULL;
+}
+
+static void disconnect(struct usb_device *dev, void *usbif)
+{
+ /* Note the device is removed so we can tell the guest when it probes. */
+ owned_port_t *port = usbif_find_port(dev->devpath);
+ port->dev_present = 0;
+ port->dev = NULL;
+ port->ifaces = 0;
+}
+
+
+struct usb_driver driver =
+{
+ .owner = THIS_MODULE,
+ .name = "Xen USB Backend",
+ .probe = probe,
+ .disconnect = disconnect,
+ .id_table = NULL,
+};
+
+/* __usbif_release_port - internal mechanics for releasing a port */
+void __usbif_release_port(owned_port_t *p)
+{
+ int i;
+
+ for ( i = 0; p->ifaces != 0; i++)
+ if ( p->ifaces & 1 << i )
+ {
+ usb_driver_release_interface(&driver, usb_ifnum_to_if(p->dev, i));
+ clear_bit(i, &p->ifaces);
+ }
+ list_del(&p->list);
+
+ /* Reset the real device. We don't simulate disconnect / probe for other
+ * drivers in this kernel because we assume the device is completely under
+ * the control of ourselves (i.e. the guest!). This should ensure that the
+ * device is in a sane state for the next customer ;-) */
+/* if ( p->dev != NULL) */
+/* usb_reset_device(p->dev); */
+
+ kfree(p);
+}
+
+
+/**
+ * usbif_release_port - stop claiming devices on a port on behalf of guest
+ */
+void usbif_release_port(usbif_be_release_port_t *msg)
+{
+ owned_port_t *p;
+
+ spin_lock_irq(&owned_ports_lock);
+ p = usbif_find_port(msg->path);
+ __usbif_release_port(p);
+ spin_unlock_irq(&owned_ports_lock);
+}
+
+void usbif_release_ports(usbif_priv_t *up)
+{
+ struct list_head *port, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&owned_ports_lock, flags);
+ list_for_each_safe(port, tmp, &owned_ports)
+ {
+ owned_port_t *p = list_entry(port, owned_port_t, list);
+ if ( p->usbif_priv == up )
+ __usbif_release_port(p);
+ }
+ spin_unlock_irqrestore(&owned_ports_lock, flags);
+}
+
+static int __init usbif_init(void)
+{
+ int i;
+
+ if ( !(xen_start_info.flags & SIF_INITDOMAIN) &&
+ !(xen_start_info.flags & SIF_USB_BE_DOMAIN) )
+ return 0;
+
+ INIT_LIST_HEAD(&owned_ports);
+
+ usb_register(&driver);
+
+ usbif_interface_init();
+
+ if ( (mmap_vstart = allocate_empty_lowmem_region(MMAP_PAGES)) == 0 )
+ BUG();
+
+ pending_cons = 0;
+ pending_prod = MAX_PENDING_REQS;
+ memset(pending_reqs, 0, sizeof(pending_reqs));
+ for ( i = 0; i < MAX_PENDING_REQS; i++ )
+ pending_ring[i] = i;
+
+ spin_lock_init(&usbio_schedule_list_lock);
+ INIT_LIST_HEAD(&usbio_schedule_list);
+
+ if ( kernel_thread(usbio_schedule, 0, CLONE_FS | CLONE_FILES) < 0 )
+ BUG();
+
+ usbif_ctrlif_init();
+
+ spin_lock_init(&owned_ports_lock);
+
+ printk("Xen USB Backend Initialised");
+
+ return 0;
+}
+
+__initcall(usbif_init);
--- /dev/null
+/*
+ * Xen Virtual USB Frontend Driver
+ *
+ * This file contains the first version of the Xen virtual USB hub
+ * that I've managed not to delete by mistake (3rd time lucky!).
+ *
+ * Based on Linux's uhci.c, original copyright notices are displayed
+ * below. Portions also (c) 2004 Intel Research Cambridge
+ * and (c) 2004 Mark Williamson
+ *
+ * Contact <mark.williamson@cl.cam.ac.uk> or
+ * <xen-devel@lists.sourceforge.net> regarding this code.
+ *
+ * Still to be (maybe) implemented:
+ * - multiple port
+ * - multiple interfaces
+ * - migration / backend restart support?
+ * - unloading support
+ *
+ * Differences to a normal host controller:
+ * - the backend does most of the mucky stuff so we don't have to do various
+ * things that are necessary for a normal host controller (e.g. FSBR).
+ * - we don't have any hardware, so status registers are simulated in software.
+ */
+
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ * - the low-level protocol (fairly simple but lots of small details)
+ * - working around the horridness of the rest
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#else
+#undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "xhci.h"
+
+#include <linux/pm.h>
+
+#include "../../../../../drivers/usb/hcd.h"
+
+#include <asm-xen/xen-public/io/usbif.h>
+#include <asm/ctrl_if.h>
+#include <asm/xen-public/io/domain_controller.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, Mark Williamson"
+#define DRIVER_DESC "Xen Virtual USB Host Controller Interface driver"
+
+/*
+ * debug = 0, no debugging messages
+ * debug = 1, dump failed URB's except for stalls
+ * debug = 2, dump all failed URB's (including stalls)
+ */
+#ifdef DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
+static char *errbuf;
+#define ERRBUF_LEN (PAGE_SIZE * 8)
+
+static kmem_cache_t *xhci_up_cachep; /* urb_priv */
+
+static int rh_submit_urb(struct urb *urb);
+static int rh_unlink_urb(struct urb *urb);
+//static int xhci_get_current_frame_number(struct usb_device *dev);
+static int xhci_unlink_urb(struct urb *urb);
+static void xhci_unlink_generic(struct urb *urb);
+static void xhci_call_completion(struct urb *urb);
+static void xhci_drain_ring(void);
+
+#define MAX_URB_LOOP 2048 /* Maximum number of linked URB's */
+
+struct xhci *xhci;
+
+enum { USBIF_STATE_CONNECTED = 2,
+ USBIF_STATE_DISCONNECTED = 1,
+ USBIF_STATE_CLOSED =0
+};
+
+static int awaiting_reset = 0;
+
+/**
+ * xhci_construct_isoc - add isochronous information to a request
+ */
+int xhci_construct_isoc(usbif_request_t *req, struct urb *urb)
+{
+ usbif_iso_t *schedule;
+ int i;
+ struct urb_priv *urb_priv = urb->hcpriv;
+
+ req->num_iso = urb->number_of_packets;
+ schedule = (usbif_iso_t *)__get_free_page(GFP_KERNEL);
+
+ if ( schedule == NULL )
+ return -ENOMEM;
+
+ for ( i = 0; i < req->num_iso; i++ )
+ {
+ schedule[i].buffer_offset = urb->iso_frame_desc[i].offset;
+ schedule[i].length = urb->iso_frame_desc[i].length;
+ }
+
+ urb_priv->schedule = schedule;
+ req->iso_schedule = virt_to_machine(schedule);
+
+ return 0;
+}
+
+#define USBIF_RING_FULL ((xhci->usbif->req_prod - xhci->usb_resp_cons) == USBIF_RING_SIZE)
+
+static void dump_urb(struct urb *urb)
+{
+ printk("dumping urb @ %p\n", urb);
+
+ printk("hcpriv = %p\n", urb->hcpriv);
+ printk("next = %p\n", urb->next);
+ printk("dev = %p\n", urb->dev);
+ printk("pipe = 0x%lx\n", urb->pipe);
+ printk("status = %d\n", urb->status);
+ printk("transfer_flags = 0x%lx\n", urb->transfer_flags);
+ printk("transfer_buffer = %p\n", urb->transfer_buffer);
+ printk("transfer_buffer_length = %d\n", urb->transfer_buffer_length);
+ printk("actual_length = %d\n", urb->actual_length);
+ printk("bandwidth = %d\n", urb->bandwidth);
+ printk("setup_packet = %p\n", urb->setup_packet);
+ if ( urb->setup_packet != NULL )
+ printk("setup = { 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
+ urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+ printk("complete = %p\n", urb->complete);
+ printk("interval = %d\n", urb->interval);
+
+}
+
+
+static int
+xhci_queue_req(struct urb *urb)
+{
+ usbif_request_t *req;
+ usbif_t *usbif = xhci->usbif;
+
+#if 0
+ printk("usbif = %p, req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
+ usbif, usbif->req_prod, virt_to_machine(&usbif->req_prod),
+ usbif->resp_prod, xhci->usb_resp_cons);
+#endif
+
+
+/* printk("Usbif_priv %p, want IO at 0x%lx\n", urb->hcpriv, virt_to_machine(urb->transfer_buffer)); */
+
+ if ( USBIF_RING_FULL )
+ {
+ printk("xhci_queue_req(): USB ring full, not queuing request\n");
+ return -ENOBUFS;
+ }
+
+ /* Stick something in the shared communications ring. */
+ req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+ req->operation = USBIF_OP_IO;
+ req->port = 0; /* We don't care what the port is. */
+ req->id = (unsigned long) urb->hcpriv;
+ req->transfer_buffer = virt_to_machine(urb->transfer_buffer);
+ req->devnum = usb_pipedevice(urb->pipe);
+ req->direction = usb_pipein(urb->pipe);
+ req->speed = usb_pipeslow(urb->pipe);
+ req->pipe_type = usb_pipetype(urb->pipe);
+ req->length = urb->transfer_buffer_length;
+ req->transfer_flags = urb->transfer_flags;
+ req->endpoint = usb_pipeendpoint(urb->pipe);
+ req->speed = usb_pipeslow(urb->pipe);
+ req->timeout = urb->timeout * (1000 / HZ);
+
+ if ( usb_pipetype(urb->pipe) == 0 ) /* ISO */
+ {
+ int ret = xhci_construct_isoc(req, urb);
+ if ( ret != 0 )
+ return ret;
+ }
+
+ if(urb->setup_packet != NULL)
+ memcpy(req->setup, urb->setup_packet, 8);
+ else
+ memset(req->setup, 0, 8);
+
+ wmb();
+
+ usbif->req_prod++;
+
+ notify_via_evtchn(xhci->evtchn);
+
+ // dump_urb(urb);
+
+ return -EINPROGRESS;
+}
+
+static inline usbif_request_t *
+xhci_queue_probe(usbif_vdev_t port)
+{
+ usbif_request_t *req;
+ usbif_t *usbif = xhci->usbif;
+
+#if 0
+ printk("queuing probe: req_prod = %d (@ 0x%lx), resp_prod = %d, resp_cons = %d\n",
+ usbif->req_prod, virt_to_machine(&usbif->req_prod),
+ usbif->resp_prod, xhci->usb_resp_cons);
+#endif
+
+ if ( USBIF_RING_FULL )
+ {
+ printk("xhci_queue_probe(): USB ring full, not queuing request\n");
+ return NULL;
+ }
+
+ /* Stick something in the shared communications ring. */
+ req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+ req->operation = USBIF_OP_PROBE;
+ req->port = port;
+ req->id = 0;
+ req->transfer_buffer = 0;
+ req->devnum = 0;
+ req->direction = 0;
+ req->speed = 0;
+ req->pipe_type = 0;
+ req->length = 0;
+ req->transfer_flags = 0;
+ req->endpoint = 0;
+ req->speed = 0;
+
+ wmb();
+
+ usbif->req_prod++;
+
+ notify_via_evtchn(xhci->evtchn);
+
+ return req;
+}
+
+static int
+xhci_port_reset(usbif_vdev_t port)
+{
+ usbif_request_t *req;
+ usbif_t *usbif = xhci->usbif;
+
+ /* We only reset one port at a time, so we only need one variable per
+ * hub. */
+ awaiting_reset = 1;
+
+ /* Stick something in the shared communications ring. */
+ req = &usbif->ring[MASK_USBIF_IDX(usbif->req_prod)].req;
+
+ req->operation = USBIF_OP_RESET;
+ req->port = port;
+
+ wmb();
+
+ usbif->req_prod++;
+
+ notify_via_evtchn(xhci->evtchn);
+
+ while ( awaiting_reset > 0 )
+ {
+ mdelay(1);
+ xhci_drain_ring();
+ }
+
+ return awaiting_reset;
+}
+
+static void xhci_show_resp(usbif_response_t *r)
+{
+ printk("id=0x%lx, op=0x%x, data=0x%x, status=0x%x, length=0x%lx\n",
+ r->id, r->operation, r->data, r->status, r->length);
+}
+
+
+/*
+ * Only the USB core should call xhci_alloc_dev and xhci_free_dev
+ */
+static int xhci_alloc_dev(struct usb_device *dev)
+{
+ return 0;
+}
+
+static int xhci_free_dev(struct usb_device *dev)
+{
+ return 0;
+}
+
+static inline void xhci_add_complete(struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xhci->complete_list_lock, flags);
+ list_add_tail(&urbp->complete_list, &xhci->complete_list);
+ spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+}
+
+/* When this returns, the owner of the URB may free its
+ * storage.
+ *
+ * We spin and wait for the URB to complete before returning.
+ */
+static void xhci_delete_urb(struct urb *urb)
+{
+ struct urb_priv *urbp;
+
+ urbp = urb->hcpriv;
+
+ /* If there's no urb_priv structure for this URB then it can't have
+ * been submitted at all. */
+ if ( urbp == NULL )
+ return;
+
+ /* For now we just spin until the URB completes. It shouldn't take too
+ * long and we don't expect to have to do this very often. */
+ while ( urb->status == -EINPROGRESS )
+ {
+ xhci_drain_ring();
+ mdelay(1);
+ }
+
+ /* Now we know that further transfers to the buffer won't
+ * occur, so we can safely return. */
+}
+
+static struct urb_priv *xhci_alloc_urb_priv(struct urb *urb)
+{
+ struct urb_priv *urbp;
+
+ urbp = kmem_cache_alloc(xhci_up_cachep, SLAB_ATOMIC);
+ if (!urbp) {
+ err("xhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n");
+ return NULL;
+ }
+
+ memset((void *)urbp, 0, sizeof(*urbp));
+
+ urbp->inserttime = jiffies;
+ urbp->urb = urb;
+ urbp->dev = urb->dev;
+
+ INIT_LIST_HEAD(&urbp->complete_list);
+
+ urb->hcpriv = urbp;
+
+ return urbp;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+/* When is this called? Do we need to stop the transfer (as we
+ * currently do)? */
+static void xhci_destroy_urb_priv(struct urb *urb)
+{
+ struct urb_priv *urbp;
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+ if (!urbp)
+ return;
+
+ if (!list_empty(&urb->urb_list))
+ warn("xhci_destroy_urb_priv: urb %p still on xhci->urb_list or xhci->remove_list", urb);
+
+ if (!list_empty(&urbp->complete_list))
+ warn("xhci_destroy_urb_priv: urb %p still on xhci->complete_list", urb);
+
+ kmem_cache_free(xhci_up_cachep, urb->hcpriv);
+
+ urb->hcpriv = NULL;
+}
+
+/**
+ * Try to find URBs in progress on the same pipe to the same device.
+ *
+ * MUST be called with xhci->urb_list_lock acquired
+ */
+static struct urb *xhci_find_urb_ep(struct xhci *xhci, struct urb *urb)
+{
+ struct list_head *tmp, *head;
+
+ /* We don't match Isoc transfers since they are special */
+ if (usb_pipeisoc(urb->pipe))
+ return NULL;
+
+ head = &xhci->urb_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+ tmp = tmp->next;
+
+ if (u->dev == urb->dev && u->pipe == urb->pipe &&
+ u->status == -EINPROGRESS)
+ return u;
+ }
+
+ return NULL;
+}
+
+static int xhci_submit_urb(struct urb *urb)
+{
+ int ret = -EINVAL;
+ unsigned long flags;
+ struct urb *eurb;
+ int bustime;
+
+#if 0
+ printk("submitting urb @ %p for dev @ %p, devnum = %d path %s\n",
+ urb, urb->dev, urb->dev->devnum, urb->dev->devpath);
+#endif
+
+ if (!urb)
+ return -EINVAL;
+
+ if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) {
+ warn("xhci_submit_urb: urb %p belongs to disconnected device or bus?", urb);
+ return -ENODEV;
+ }
+
+ if ( urb->dev->devpath == NULL )
+ {
+ printk("BARF!\n");
+ BUG();
+ }
+
+
+
+ usb_inc_dev_use(urb->dev);
+
+ spin_lock_irqsave(&xhci->urb_list_lock, flags);
+ spin_lock(&urb->lock);
+
+ if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET ||
+ urb->status == -ECONNABORTED) {
+ dbg("xhci_submit_urb: urb not available to submit (status = %d)", urb->status);
+ /* Since we can have problems on the out path */
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+ usb_dec_dev_use(urb->dev);
+
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&urb->urb_list);
+ if (!xhci_alloc_urb_priv(urb)) {
+ ret = -ENOMEM;
+
+ goto out;
+ }
+
+ ( (struct urb_priv *)urb->hcpriv )->in_progress = 1;
+
+ eurb = xhci_find_urb_ep(xhci, urb);
+ if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) {
+ ret = -ENXIO;
+
+ goto out;
+ }
+
+ /* Short circuit the virtual root hub */
+ if (urb->dev == xhci->rh.dev) {
+ ret = rh_submit_urb(urb);
+
+ goto out;
+ }
+
+ if ( usb_pipedevice(urb->pipe) == 1 )
+ printk("dev = %p, dev->path = %s, rh.dev = %p, rh.dev.devnum = %d rh.dev->path = %s!\n",
+ urb->dev, urb->dev->devpath, xhci->rh.dev, xhci->rh.dev->devnum, xhci->rh.dev->devpath);
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ ret = xhci_queue_req(urb);
+ break;
+ case PIPE_INTERRUPT:
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0)
+ ret = bustime;
+ else {
+ ret = xhci_queue_req(urb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+ }
+ } else /* bandwidth is already set */
+ ret = xhci_queue_req(urb);
+ break;
+ case PIPE_BULK:
+ ret = xhci_queue_req(urb);
+ break;
+ case PIPE_ISOCHRONOUS:
+ if (urb->bandwidth == 0) { /* not yet checked/allocated */
+ if (urb->number_of_packets <= 0) {
+ ret = -EINVAL;
+ break;
+ }
+ bustime = usb_check_bandwidth(urb->dev, urb);
+ if (bustime < 0) {
+ ret = bustime;
+ break;
+ }
+
+ ret = xhci_queue_req(urb);
+ if (ret == -EINPROGRESS)
+ usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+ } else /* bandwidth is already set */
+ ret = xhci_queue_req(urb);
+ break;
+ }
+
+out:
+ urb->status = ret;
+
+ if (ret == -EINPROGRESS) {
+ /* We use _tail to make find_urb_ep more efficient */
+ list_add_tail(&urb->urb_list, &xhci->urb_list);
+
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ return 0;
+ }
+
+ xhci_unlink_generic(urb);
+
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ /* Only call completion if it was successful */
+ if (!ret)
+ xhci_call_completion(urb);
+
+ return ret;
+}
+
+/*
+ * Return the result of a transfer
+ *
+ * MUST be called with urb_list_lock acquired
+ */
+static void xhci_transfer_result(struct xhci *xhci, struct urb *urb)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct urb_priv *urbp;
+
+ /* The root hub is special */
+ if (urb->dev == xhci->rh.dev)
+ return;
+
+ spin_lock_irqsave(&urb->lock, flags);
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+
+ if ( ( (struct urb_priv *)urb->hcpriv )->in_progress )
+ ret = -EINPROGRESS;
+
+ if (urb->actual_length < urb->transfer_buffer_length) {
+ if (urb->transfer_flags & USB_DISABLE_SPD) {
+ ret = -EREMOTEIO;
+ }
+ }
+
+ if (urb->status == -EPIPE)
+ {
+ ret = urb->status;
+ /* endpoint has stalled - mark it halted */
+ usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe),
+ usb_pipeout(urb->pipe));
+ }
+
+ if ((debug == 1 && ret != 0 && ret != -EPIPE) ||
+ (ret != 0 && debug > 1)) {
+ /* Some debugging code */
+ dbg("xhci_result_interrupt/bulk() failed with status %x",
+ status);
+ }
+
+ if (ret == -EINPROGRESS)
+ goto out;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ case PIPE_ISOCHRONOUS:
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth)
+ usb_release_bandwidth(urb->dev, urb, 1);
+ xhci_unlink_generic(urb);
+ break;
+ case PIPE_INTERRUPT:
+ /* Interrupts are an exception */
+ if (urb->interval)
+ goto out_complete;
+
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth)
+ usb_release_bandwidth(urb->dev, urb, 0);
+ xhci_unlink_generic(urb);
+ break;
+ default:
+ info("xhci_transfer_result: unknown pipe type %d for urb %p\n",
+ usb_pipetype(urb->pipe), urb);
+ }
+
+ /* Remove it from xhci->urb_list */
+ list_del_init(&urb->urb_list);
+
+out_complete:
+ xhci_add_complete(urb);
+
+out:
+ spin_unlock_irqrestore(&urb->lock, flags);
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static void xhci_unlink_generic(struct urb *urb)
+{
+ struct urb_priv *urbp = urb->hcpriv;
+
+ /* We can get called when urbp allocation fails, so check */
+ if (!urbp)
+ return;
+
+ /* ??? This function is now so minimal it doesn't do much. Do we really
+ * need it? */
+
+ xhci_delete_urb(urb);
+}
+
+static int xhci_unlink_urb(struct urb *urb)
+{
+ unsigned long flags;
+ struct urb_priv *urbp = urb->hcpriv;
+
+ if (!urb)
+ return -EINVAL;
+
+ if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv)
+ return -ENODEV;
+
+ spin_lock_irqsave(&xhci->urb_list_lock, flags);
+ spin_lock(&urb->lock);
+
+ /* Release bandwidth for Interrupt or Isoc. transfers */
+ /* Spinlock needed ? */
+ if (urb->bandwidth) {
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_release_bandwidth(urb->dev, urb, 0);
+ break;
+ case PIPE_ISOCHRONOUS:
+ usb_release_bandwidth(urb->dev, urb, 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (urb->status != -EINPROGRESS) {
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+ return 0;
+ }
+
+ list_del_init(&urb->urb_list);
+
+ xhci_unlink_generic(urb);
+
+ /* Short circuit the virtual root hub */
+ if (urb->dev == xhci->rh.dev) {
+ rh_unlink_urb(urb);
+
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ xhci_call_completion(urb);
+ } else {
+ if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+ urbp->status = urb->status = -ECONNABORTED;
+
+ spin_lock(&xhci->urb_remove_list_lock);
+
+ list_add(&urb->urb_list, &xhci->urb_remove_list);
+
+ spin_unlock(&xhci->urb_remove_list_lock);
+
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ } else {
+ urb->status = -ENOENT;
+
+ spin_unlock(&urb->lock);
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ if (in_interrupt()) { /* wait at least 1 frame */
+ static int errorcount = 10;
+
+ if (errorcount--)
+ dbg("xhci_unlink_urb called from interrupt for urb %p", urb);
+ udelay(1000);
+ } else
+ schedule_timeout(1+1*HZ/1000);
+
+ xhci_call_completion(urb);
+ }
+ }
+
+ return 0;
+}
+
+
+struct usb_operations xhci_device_operations = {
+ .allocate = xhci_alloc_dev,
+ .deallocate = xhci_free_dev,
+ /* It doesn't look like any drivers actually care what the frame number
+ * is at the moment! If necessary, we could approximate the current
+ * frame nubmer by passing it from the backend in response messages. */
+ .get_frame_number = NULL,
+ .submit_urb = xhci_submit_urb,
+ .unlink_urb = xhci_unlink_urb
+};
+
+/* Virtual Root Hub */
+
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __u16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered,
+ Bit 5 Remote-wakeup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x29, /* __u8 bDescriptorType; Hub-descriptor */
+ 0x02, /* __u8 bNbrPorts; */
+ 0x00, /* __u16 wHubCharacteristics; */
+ 0x00,
+ 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
+ 0x00, /* __u8 bHubContrCurrent; 0 mA */
+ 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
+ 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */
+static int rh_send_irq(struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ xhci_port_t *ports = xhci->rh.ports;
+ unsigned long flags;
+ int i, len = 1;
+ __u16 data = 0;
+
+ spin_lock_irqsave(&urb->lock, flags);
+ for (i = 0; i < xhci->rh.numports; i++) {
+ /* MAW: No idea what the old code was doing here or why it worked.
+ * This implementation sets a bit if anything at all has changed on the
+ * port, as per USB spec 11.12 */
+ data |= (ports[i].cs_chg || ports[i].pe_chg )
+ ? (1 << (i + 1))
+ : 0;
+
+ len = (i + 1) / 8 + 1;
+ }
+
+ *(__u16 *) urb->transfer_buffer = cpu_to_le16(data);
+ urb->actual_length = len;
+ urbp->status = 0;
+
+ spin_unlock_irqrestore(&urb->lock, flags);
+
+ if ((data > 0) && (xhci->rh.send != 0)) {
+ dbg("root-hub INT complete: data: %x", data);
+ xhci_call_completion(urb);
+ }
+
+ return 0;
+}
+
+/* Virtual Root Hub INTs are polled by this timer every "interval" ms */
+static int rh_init_int_timer(struct urb *urb);
+
+static void rh_int_timer_do(unsigned long ptr)
+{
+ struct urb *urb = (struct urb *)ptr;
+ struct list_head list, *tmp, *head;
+ unsigned long flags;
+ int i;
+
+ for ( i = 0; i < xhci->rh.numports; i++)
+ xhci_queue_probe(i);
+
+ if (xhci->rh.send)
+ rh_send_irq(urb);
+
+ INIT_LIST_HEAD(&list);
+
+ spin_lock_irqsave(&xhci->urb_list_lock, flags);
+ head = &xhci->urb_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb *u = list_entry(tmp, struct urb, urb_list);
+ struct urb_priv *up = (struct urb_priv *)u->hcpriv;
+
+ tmp = tmp->next;
+
+ spin_lock(&u->lock);
+
+ /* Check if the URB timed out */
+ if (u->timeout && time_after_eq(jiffies, up->inserttime + u->timeout)) {
+ list_del(&u->urb_list);
+ list_add_tail(&u->urb_list, &list);
+ }
+
+ spin_unlock(&u->lock);
+ }
+ spin_unlock_irqrestore(&xhci->urb_list_lock, flags);
+
+ head = &list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb *u = list_entry(tmp, struct urb, urb_list);
+
+ tmp = tmp->next;
+
+ u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED;
+ xhci_unlink_urb(u);
+ }
+
+ rh_init_int_timer(urb);
+}
+
+/* Root Hub INTs are polled by this timer */
+static int rh_init_int_timer(struct urb *urb)
+{
+ xhci->rh.interval = urb->interval;
+ init_timer(&xhci->rh.rh_int_timer);
+ xhci->rh.rh_int_timer.function = rh_int_timer_do;
+ xhci->rh.rh_int_timer.data = (unsigned long)urb;
+ xhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+ add_timer(&xhci->rh.rh_int_timer);
+
+ return 0;
+}
+
+#define OK(x) len = (x); break
+
+/* Root Hub Control Pipe */
+static int rh_submit_urb(struct urb *urb)
+{
+ unsigned int pipe = urb->pipe;
+ struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+ void *data = urb->transfer_buffer;
+ int leni = urb->transfer_buffer_length;
+ int len = 0;
+ xhci_port_t *status;
+ int stat = 0;
+ int i;
+ int retstatus;
+ unsigned long flags;
+
+ __u16 cstatus;
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+ if (usb_pipetype(pipe) == PIPE_INTERRUPT) {
+ xhci->rh.urb = urb;
+ xhci->rh.send = 1;
+ xhci->rh.interval = urb->interval;
+ rh_init_int_timer(urb);
+
+ return -EINPROGRESS;
+ }
+
+ bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8;
+ wValue = le16_to_cpu(cmd->wValue);
+ wIndex = le16_to_cpu(cmd->wIndex);
+ wLength = le16_to_cpu(cmd->wLength);
+
+ for (i = 0; i < 8; i++)
+ xhci->rh.c_p_r[i] = 0;
+
+ status = &xhci->rh.ports[wIndex - 1];
+
+ spin_lock_irqsave(&xhci->rh.port_state_lock, flags);
+
+ switch (bmRType_bReq) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ *(__u16 *)data = cpu_to_le16(1);
+ OK(2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *)data = cpu_to_le16(0);
+ OK(2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *)data = cpu_to_le16(0);
+ OK(2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *)data = cpu_to_le32(0);
+ OK(4); /* hub power */
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ cstatus = (status->cs_chg) |
+ (status->pe_chg << 1) |
+ (xhci->rh.c_p_r[wIndex - 1] << 4);
+ retstatus = (status->ccs) |
+ (status->pe << 1) |
+ (status->susp << 2) |
+ (status->pr << 8) |
+ (1 << 8) | /* power on */
+ (status->lsda << 9);
+ *(__u16 *)data = cpu_to_le16(retstatus);
+ *(__u16 *)(data + 2) = cpu_to_le16(cstatus);
+ OK(4);
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case RH_ENDPOINT_STALL:
+ OK(0);
+ }
+ break;
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case RH_C_HUB_OVER_CURRENT:
+ OK(0); /* hub power over current */
+ }
+ break;
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case RH_PORT_ENABLE:
+ status->pe = 0;
+ OK(0);
+ case RH_PORT_SUSPEND:
+ status->susp = 0;
+ OK(0);
+ case RH_PORT_POWER:
+ OK(0); /* port power */
+ case RH_C_PORT_CONNECTION:
+ status->cs_chg = 0;
+ OK(0);
+ case RH_C_PORT_ENABLE:
+ status->pe_chg = 0;
+ OK(0);
+ case RH_C_PORT_SUSPEND:
+ /*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+ OK(0);
+ case RH_C_PORT_OVER_CURRENT:
+ OK(0); /* port power over current */
+ case RH_C_PORT_RESET:
+ xhci->rh.c_p_r[wIndex - 1] = 0;
+ OK(0);
+ }
+ break;
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case RH_PORT_SUSPEND:
+ status->susp = 1;
+ OK(0);
+ case RH_PORT_RESET:
+ {
+ int ret;
+ xhci->rh.c_p_r[wIndex - 1] = 1;
+ status->pr = 0;
+ status->pe = 1;
+ ret = xhci_port_reset(wIndex - 1);
+ /* XXX MAW: should probably cancel queued transfers during reset... *\/ */
+ if ( ret == 0 ) { OK(0); }
+ else { return ret; }
+ }
+ break;
+ case RH_PORT_POWER:
+ OK(0); /* port power ** */
+ case RH_PORT_ENABLE:
+ status->pe = 1;
+ OK(0);
+ }
+ break;
+ case RH_SET_ADDRESS:
+ printk("setting root hub device to %d\n", wValue);
+ xhci->rh.devnum = wValue;
+ OK(0);
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case 0x01: /* device descriptor */
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int,
+ sizeof(root_hub_dev_des), wLength));
+ memcpy(data, root_hub_dev_des, len);
+ OK(len);
+ case 0x02: /* configuration descriptor */
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int,
+ sizeof(root_hub_config_des), wLength));
+ memcpy (data, root_hub_config_des, len);
+ OK(len);
+ case 0x03: /* string descriptors */
+ len = usb_root_hub_string (wValue & 0xff,
+ 0, "XHCI-alt",
+ data, wLength);
+ if (len > 0) {
+ OK(min_t(int, leni, len));
+ } else
+ stat = -EPIPE;
+ }
+ break;
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ root_hub_hub_des[2] = xhci->rh.numports;
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int, sizeof(root_hub_hub_des), wLength));
+ memcpy(data, root_hub_hub_des, len);
+ OK(len);
+ case RH_GET_CONFIGURATION:
+ *(__u8 *)data = 0x01;
+ OK(1);
+ case RH_SET_CONFIGURATION:
+ OK(0);
+ case RH_GET_INTERFACE | RH_INTERFACE:
+ *(__u8 *)data = 0x00;
+ OK(1);
+ case RH_SET_INTERFACE | RH_INTERFACE:
+ OK(0);
+ default:
+ stat = -EPIPE;
+ }
+
+ spin_unlock_irqrestore(&xhci->rh.port_state_lock, flags);
+
+ urb->actual_length = len;
+
+ return stat;
+}
+
+/*
+ * MUST be called with urb->lock acquired
+ */
+static int rh_unlink_urb(struct urb *urb)
+{
+ if (xhci->rh.urb == urb) {
+ urb->status = -ENOENT;
+ xhci->rh.send = 0;
+ xhci->rh.urb = NULL;
+ del_timer(&xhci->rh.rh_int_timer);
+ }
+ return 0;
+}
+
+static void xhci_call_completion(struct urb *urb)
+{
+ struct urb_priv *urbp;
+ struct usb_device *dev = urb->dev;
+ int is_ring = 0, killed, resubmit_interrupt, status;
+ struct urb *nurb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&urb->lock, flags);
+
+ urbp = (struct urb_priv *)urb->hcpriv;
+ if (!urbp || !urb->dev) {
+ spin_unlock_irqrestore(&urb->lock, flags);
+ return;
+ }
+
+ killed = (urb->status == -ENOENT || urb->status == -ECONNABORTED ||
+ urb->status == -ECONNRESET);
+ resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT &&
+ urb->interval);
+
+ nurb = urb->next;
+ if (nurb && !killed) {
+ int count = 0;
+
+ while (nurb && nurb != urb && count < MAX_URB_LOOP) {
+ if (nurb->status == -ENOENT ||
+ nurb->status == -ECONNABORTED ||
+ nurb->status == -ECONNRESET) {
+ killed = 1;
+ break;
+ }
+
+ nurb = nurb->next;
+ count++;
+ }
+
+ if (count == MAX_URB_LOOP)
+ err("xhci_call_completion: too many linked URB's, loop? (first loop)");
+
+ /* Check to see if chain is a ring */
+ is_ring = (nurb == urb);
+ }
+
+ status = urbp->status;
+ if (!resubmit_interrupt || killed)
+ /* We don't need urb_priv anymore */
+ xhci_destroy_urb_priv(urb);
+
+ if (!killed)
+ urb->status = status;
+
+ spin_unlock_irqrestore(&urb->lock, flags);
+
+ if (urb->complete)
+ urb->complete(urb);
+
+ if (resubmit_interrupt)
+ /* Recheck the status. The completion handler may have */
+ /* unlinked the resubmitting interrupt URB */
+ killed = (urb->status == -ENOENT ||
+ urb->status == -ECONNABORTED ||
+ urb->status == -ECONNRESET);
+
+ if (resubmit_interrupt && !killed) {
+ if ( urb->dev != xhci->rh.dev )
+ xhci_queue_req(urb); /* XXX What if this fails? */
+ /* Don't need to resubmit URBs for the virtual root dev. */
+ } else {
+ if (is_ring && !killed) {
+ urb->dev = dev;
+ xhci_submit_urb(urb);
+ } else {
+ /* We decrement the usage count after we're done */
+ /* with everything */
+ usb_dec_dev_use(dev);
+ }
+ }
+}
+
+static void xhci_finish_completion(void)
+{
+ struct list_head *tmp, *head;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xhci->complete_list_lock, flags);
+ head = &xhci->complete_list;
+ tmp = head->next;
+ while (tmp != head) {
+ struct urb_priv *urbp = list_entry(tmp, struct urb_priv, complete_list);
+ struct urb *urb = urbp->urb;
+
+ list_del_init(&urbp->complete_list);
+ spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+
+ xhci_call_completion(urb);
+
+ spin_lock_irqsave(&xhci->complete_list_lock, flags);
+ head = &xhci->complete_list;
+ tmp = head->next;
+ }
+ spin_unlock_irqrestore(&xhci->complete_list_lock, flags);
+}
+
+void receive_usb_reset(usbif_response_t *resp)
+{
+ awaiting_reset = resp->status;
+ rmb();
+
+}
+
+void receive_usb_probe(usbif_response_t *resp)
+{
+ spin_lock(&xhci->rh.port_state_lock);
+
+ if ( resp->status > 0 )
+ {
+ if ( resp->status == 1 )
+ {
+/* printk("hey hey, there's a device on port %d\n", resp->data); */
+
+ /* If theres a device there and there wasn't one before there must
+ * have been a connection status change. */
+ if( xhci->rh.ports[resp->data].cs == 0 )
+ {
+ xhci->rh.ports[resp->data].cs = 1;
+ xhci->rh.ports[resp->data].ccs = 1;
+ xhci->rh.ports[resp->data].cs_chg = 1;
+/* printk("Look at device on port %d that wasn't there before\n", resp->data); */
+ }
+ }
+ else
+ printk("receive_usb_probe(): unexpected status %d for port %d\n",
+ resp->status, resp->data);
+ }
+ else if ( resp->status < 0)
+ printk("receive_usb_probe(): got error status %d\n", resp->status);
+
+ spin_unlock(&xhci->rh.port_state_lock);
+}
+
+void receive_usb_io(usbif_response_t *resp)
+{
+ struct urb_priv *urbp = (struct urb_priv *)resp->id;
+ struct urb *urb = urbp->urb;
+
+ urb->actual_length = resp->length;
+ urb->status = resp->status;
+ urbp->status = resp->status;
+ urbp->in_progress = 0;
+
+ if( usb_pipetype(urb->pipe) == 0 ) /* ISO */
+ {
+ int i;
+
+ /* Copy ISO schedule results back in. */
+
+ for ( i = 0; i < urb->number_of_packets; i++ )
+ {
+ urb->iso_frame_desc[i].status
+ = urbp->schedule[i].status;
+ urb->iso_frame_desc[i].actual_length
+ = urbp->schedule[i].length;
+ }
+ free_page((unsigned long)urbp->schedule);
+ }
+}
+
+static void xhci_drain_ring(void)
+{
+ struct list_head *tmp, *head;
+ usbif_t *usb_ring = xhci->usbif;
+ usbif_response_t *resp;
+ USBIF_RING_IDX i, rp;
+
+ /* Walk the ring here to get responses, updating URBs to show what
+ * completed. */
+
+ rp = usb_ring->resp_prod;
+ rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ /* Take items off the comms ring, taking care not to overflow. */
+ for ( i = xhci->usb_resp_cons;
+ (i != rp) && ((i-usb_ring->req_prod) != USBIF_RING_SIZE);
+ i++ )
+ {
+ resp = &usb_ring->ring[MASK_USBIF_IDX(i)].resp;
+
+ /* May need to deal with batching and with putting a ceiling on
+ the number dispatched for performance and anti-dos reasons */
+
+#if 0
+ printk("usbfront: Processing response:\n");
+ printk(" id = 0x%x\n", resp->id);
+ printk(" op = %d\n", resp->operation);
+ printk(" status = %d\n", resp->status);
+ printk(" length = %d\n", resp->length);
+#endif
+
+ switch ( resp->operation )
+ {
+ case USBIF_OP_PROBE:
+ receive_usb_probe(resp);
+ break;
+
+ case USBIF_OP_IO:
+ receive_usb_io(resp);
+ break;
+
+ case USBIF_OP_RESET:
+ receive_usb_reset(resp);
+ break;
+
+ default:
+ printk("error: unknown USB io operation response [%d]\n",
+ usb_ring->ring[i].req.operation);
+ break;
+ }
+ }
+
+ xhci->usb_resp_cons = i;
+
+ /* Walk the list of pending URB's to see which ones completed and do
+ * callbacks, etc. */
+ spin_lock(&xhci->urb_list_lock);
+ head = &xhci->urb_list;
+ tmp = head->next;
+ while (tmp != head) {
+
+ struct urb *urb = list_entry(tmp, struct urb, urb_list);
+
+ tmp = tmp->next;
+
+ /* Checks the status and does all of the magic necessary */
+ xhci_transfer_result(xhci, urb);
+ }
+ spin_unlock(&xhci->urb_list_lock);
+
+ xhci_finish_completion();
+}
+
+
+static void xhci_interrupt(int irq, void *__xhci, struct pt_regs *regs)
+{
+ xhci_drain_ring();
+}
+
+static void free_xhci(struct xhci *xhci)
+{
+ kfree(xhci);
+}
+
+/* /\* */
+/* * De-allocate all resources.. */
+/* *\/ */
+/* static void release_xhci(struct xhci *xhci) */
+/* { */
+/* if (xhci->irq >= 0) { */
+/* free_irq(xhci->irq, xhci); */
+/* xhci->irq = -1; */
+/* } */
+
+/* /\* Get the ring back from the backend domain. Then free it. Hmmmm. */
+/* * Lets ignore this for now - not particularly useful. *\/ */
+
+/* free_xhci(xhci); */
+/* } */
+
+/**
+ * Initialise a new virtual root hub for a new USB device channel.
+ */
+static int alloc_xhci(void)
+{
+ int retval;
+ struct usb_bus *bus;
+
+ retval = -EBUSY;
+
+ xhci = kmalloc(sizeof(*xhci), GFP_KERNEL);
+ if (!xhci) {
+ err("couldn't allocate xhci structure");
+ retval = -ENOMEM;
+ goto err_alloc_xhci;
+ }
+
+ /* Reset here so we don't get any interrupts from an old setup */
+ /* or broken setup */
+ // reset_hc(xhci);
+
+
+ xhci->state = USBIF_STATE_CLOSED;
+ xhci->is_suspended = 0;
+
+ spin_lock_init(&xhci->urb_remove_list_lock);
+ INIT_LIST_HEAD(&xhci->urb_remove_list);
+
+ spin_lock_init(&xhci->urb_list_lock);
+ INIT_LIST_HEAD(&xhci->urb_list);
+
+ spin_lock_init(&xhci->complete_list_lock);
+ INIT_LIST_HEAD(&xhci->complete_list);
+
+ spin_lock_init(&xhci->frame_list_lock);
+
+ /* We need exactly one page (per XHCI specs), how convenient */
+ /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
+#if PAGE_SIZE < (4 * 1024)
+#error PAGE_SIZE is not atleast 4k
+#endif
+ bus = usb_alloc_bus(&xhci_device_operations);
+ if (!bus) {
+ err("unable to allocate bus");
+ goto err_alloc_bus;
+ }
+
+ xhci->bus = bus;
+ bus->bus_name = "XHCI";
+ bus->hcpriv = xhci;
+
+ usb_register_bus(xhci->bus);
+
+ /* Initialize the root hub */
+
+ xhci->rh.numports = 0;
+
+ xhci->bus->root_hub = xhci->rh.dev = usb_alloc_dev(NULL, xhci->bus);
+ if (!xhci->rh.dev) {
+ err("unable to allocate root hub");
+ goto err_alloc_root_hub;
+ }
+
+ xhci->state = 0;
+
+ return 0;
+
+/*
+ * error exits:
+ */
+err_start_root_hub:
+ free_irq(xhci->irq, xhci);
+ xhci->irq = -1;
+
+err_alloc_root_hub:
+ usb_free_bus(xhci->bus);
+ xhci->bus = NULL;
+
+err_alloc_bus:
+ free_xhci(xhci);
+
+err_alloc_xhci:
+ return retval;
+}
+
+static void usbif_status_change(usbif_fe_interface_status_changed_t *status)
+{
+ ctrl_msg_t cmsg;
+ usbif_fe_interface_connect_t up;
+ long rc;
+ usbif_t *usbif;
+
+ switch ( status->status )
+ {
+ case USBIF_INTERFACE_STATUS_DESTROYED:
+ printk(KERN_WARNING "Unexpected usbif-DESTROYED message in state %d\n",
+ xhci->state);
+ break;
+
+ case USBIF_INTERFACE_STATUS_DISCONNECTED:
+ if ( xhci->state != USBIF_STATE_CLOSED )
+ {
+ printk(KERN_WARNING "Unexpected usbif-DISCONNECTED message"
+ " in state %d\n", xhci->state);
+ break;
+ /* Not bothering to do recovery here for now. Keep things
+ * simple. */
+ }
+
+ /* Move from CLOSED to DISCONNECTED state. */
+ xhci->usbif = usbif = (usbif_t *)__get_free_page(GFP_KERNEL);
+ usbif->req_prod = usbif->resp_prod = 0;
+ xhci->state = USBIF_STATE_DISCONNECTED;
+
+ /* Construct an interface-CONNECT message for the domain controller. */
+ cmsg.type = CMSG_USBIF_FE;
+ cmsg.subtype = CMSG_USBIF_FE_INTERFACE_CONNECT;
+ cmsg.length = sizeof(usbif_fe_interface_connect_t);
+ up.shmem_frame = virt_to_machine(usbif) >> PAGE_SHIFT;
+ memcpy(cmsg.msg, &up, sizeof(up));
+
+ /* Tell the controller to bring up the interface. */
+ ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+ break;
+
+ case USBIF_INTERFACE_STATUS_CONNECTED:
+ if ( xhci->state == USBIF_STATE_CLOSED )
+ {
+ printk(KERN_WARNING "Unexpected usbif-CONNECTED message"
+ " in state %d\n", xhci->state);
+ break;
+ }
+
+ xhci->evtchn = status->evtchn;
+ xhci->irq = bind_evtchn_to_irq(xhci->evtchn);
+ xhci->bandwidth = status->bandwidth;
+ xhci->rh.numports = status->num_ports;
+
+ xhci->rh.ports = kmalloc (sizeof(xhci_port_t) * xhci->rh.numports, GFP_KERNEL);
+ memset(xhci->rh.ports, 0, sizeof(xhci_port_t) * xhci->rh.numports);
+
+ printk("rh.dev @ %p\n", xhci->rh.dev);
+
+ usb_connect(xhci->rh.dev);
+
+ if (usb_new_device(xhci->rh.dev) != 0) {
+ err("unable to start root hub");
+ }
+
+ /* Allocate the appropriate USB bandwidth here... Need to
+ * somehow know what the total available is thought to be so we
+ * can calculate the reservation correctly. */
+ usb_claim_bandwidth(xhci->rh.dev, xhci->rh.urb,
+ 1000 - xhci->bandwidth, 0);
+
+ if ( (rc = request_irq(xhci->irq, xhci_interrupt,
+ SA_SAMPLE_RANDOM, "usbif", xhci)) )
+ printk(KERN_ALERT"usbfront request_irq failed (%ld)\n",rc);
+
+ printk(KERN_INFO __FILE__ ": USB XHCI: SHM at %p (0x%lx), EVTCHN %d IRQ %d\n",
+ xhci->usbif, virt_to_machine(xhci->usbif), xhci->evtchn, xhci->irq);
+
+ xhci->state = USBIF_STATE_CONNECTED;
+
+ break;
+
+ default:
+ printk(KERN_WARNING "Status change to unknown value %d\n",
+ status->status);
+ break;
+ }
+}
+
+
+static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
+{
+ switch ( msg->subtype )
+ {
+ case CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED:
+ if ( msg->length != sizeof(usbif_fe_interface_status_changed_t) )
+ goto parse_error;
+ usbif_status_change((usbif_fe_interface_status_changed_t *)
+ &msg->msg[0]);
+ break;
+
+ /* New interface...? */
+ default:
+ goto parse_error;
+ }
+
+ ctrl_if_send_response(msg);
+ return;
+
+ parse_error:
+ msg->length = 0;
+ ctrl_if_send_response(msg);
+}
+
+
+static int __init xhci_hcd_init(void)
+{
+ int retval = -ENOMEM, i;
+ usbif_fe_interface_status_changed_t st;
+ control_msg_t cmsg;
+
+ if ( (xen_start_info.flags & SIF_INITDOMAIN)
+ || (xen_start_info.flags & SIF_USB_BE_DOMAIN) )
+ return 0;
+
+ info(DRIVER_DESC " " DRIVER_VERSION);
+
+ if (debug) {
+ errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
+ if (!errbuf)
+ goto errbuf_failed;
+ }
+
+ xhci_up_cachep = kmem_cache_create("xhci_urb_priv",
+ sizeof(struct urb_priv), 0, 0, NULL, NULL);
+ if (!xhci_up_cachep)
+ goto up_failed;
+
+ /* Lazily avoid unloading issues for now. ;-)*/
+ MOD_INC_USE_COUNT;
+
+ /* Let the domain controller know we're here. For now we wait until
+ * connection, as for the block and net drivers. This is only strictly
+ * necessary if we're going to boot off a USB device. */
+ printk(KERN_INFO "Initialising Xen virtual USB hub\n");
+
+ (void)ctrl_if_register_receiver(CMSG_USBIF_FE, usbif_ctrlif_rx,
+ CALLBACK_IN_BLOCKING_CONTEXT);
+
+ alloc_xhci();
+
+ /* Send a driver-UP notification to the domain controller. */
+ cmsg.type = CMSG_USBIF_FE;
+ cmsg.subtype = CMSG_USBIF_FE_DRIVER_STATUS_CHANGED;
+ cmsg.length = sizeof(usbif_fe_driver_status_changed_t);
+ st.status = USBIF_DRIVER_STATUS_UP;
+ memcpy(cmsg.msg, &st, sizeof(st));
+ ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
+
+ /*
+ * We should read 'nr_interfaces' from response message and wait
+ * for notifications before proceeding. For now we assume that we
+ * will be notified of exactly one interface.
+ */
+ for ( i=0; (xhci->state != USBIF_STATE_CONNECTED) && (i < 10*HZ); i++ )
+ {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ if (xhci->state != USBIF_STATE_CONNECTED)
+ printk(KERN_INFO "Timeout connecting USB frontend driver!\n");
+
+ return 0;
+
+up_failed:
+
+ if (errbuf)
+ kfree(errbuf);
+
+errbuf_failed:
+
+ return retval;
+}
+
+static void __exit xhci_hcd_cleanup(void)
+{
+ if (kmem_cache_destroy(xhci_up_cachep))
+ printk(KERN_INFO "xhci: not all urb_priv's were freed\n");
+
+// release_xhci(); do some calls here
+
+
+ if (errbuf)
+ kfree(errbuf);
+}
+
+module_init(xhci_hcd_init);
+module_exit(xhci_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
--- /dev/null
+#ifndef __LINUX_XHCI_H
+#define __LINUX_XHCI_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <asm-xen/xen-public/io/usbif.h>
+#include <linux/spinlock.h>
+
+#define XHCI_NUMFRAMES 1024 /* in the frame list [array] */
+#define XHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */
+#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */
+
+/* In the absence of actual hardware state, we maintain the current known state
+ * of the virtual hub ports in this data structure.
+ */
+typedef struct
+{
+ unsigned int cs :1; /* Connection status. do we really need this /and/ ccs? */
+ unsigned int cs_chg :1; /* Connection status change. */
+ unsigned int pe :1; /* Port enable. */
+ unsigned int pe_chg :1; /* Port enable change. */
+ unsigned int ccs :1; /* Current connect status. */
+ unsigned int susp :1; /* Suspended. */
+ unsigned int lsda :1; /* Low speed device attached. */
+ unsigned int pr :1; /* Port reset. */
+
+ /* Device info? */
+} xhci_port_t;
+
+struct xhci_frame_list {
+ __u32 frame[XHCI_NUMFRAMES];
+
+ void *frame_cpu[XHCI_NUMFRAMES];
+};
+
+struct urb_priv;
+
+#define xhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000)
+#define xhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */
+
+#define xhci_maxlen(token) ((token) >> 21)
+#define xhci_expected_length(info) (((info >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) /* 1-based */
+#define xhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1)
+#define xhci_endpoint(token) (((token) >> 15) & 0xf)
+#define xhci_devaddr(token) (((token) >> 8) & 0x7f)
+#define xhci_devep(token) (((token) >> 8) & 0x7ff)
+#define xhci_packetid(token) ((token) & TD_TOKEN_PID_MASK)
+#define xhci_packetout(token) (xhci_packetid(token) != USB_PID_IN)
+#define xhci_packetin(token) (xhci_packetid(token) == USB_PID_IN)
+
+struct virt_root_hub {
+ struct usb_device *dev;
+ int devnum; /* Address of Root Hub endpoint */
+ struct urb *urb;
+ void *int_addr;
+ int send;
+ int interval;
+ int numports;
+ int c_p_r[8];
+ struct timer_list rh_int_timer;
+ spinlock_t port_state_lock;
+ xhci_port_t *ports; /* */
+};
+
+/*
+ * This describes the full xhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct xhci {
+
+#ifdef CONFIG_PROC_FS
+ /* procfs */
+ int num;
+ struct proc_dir_entry *proc_entry;
+#endif
+
+ int evtchn; /* Interdom channel to backend */
+ int irq; /* Bound to evtchn */
+ int state; /* State of this USB interface */
+ unsigned long bandwidth;
+ int handle;
+
+ struct usb_bus *bus;
+
+ spinlock_t frame_list_lock;
+ struct xhci_frame_list *fl; /* P: xhci->frame_list_lock */
+ int is_suspended;
+
+ /* Main list of URB's currently controlled by this HC */
+ spinlock_t urb_list_lock;
+ struct list_head urb_list; /* P: xhci->urb_list_lock */
+
+ /* List of asynchronously unlinked URB's */
+ spinlock_t urb_remove_list_lock;
+ struct list_head urb_remove_list; /* P: xhci->urb_remove_list_lock */
+
+ /* List of URB's awaiting completion callback */
+ spinlock_t complete_list_lock;
+ struct list_head complete_list; /* P: xhci->complete_list_lock */
+
+ struct virt_root_hub rh; /* private data of the virtual root hub */
+
+ spinlock_t response_lock;
+
+ usbif_t *usbif;
+ int usb_resp_cons;
+};
+
+struct urb_priv {
+ struct urb *urb;
+ usbif_iso_t *schedule;
+ struct usb_device *dev;
+
+ int in_progress : 1; /* QH was queued (not linked in) */
+ int short_control_packet : 1; /* If we get a short packet during */
+ /* a control transfer, retrigger */
+ /* the status phase */
+
+ int status; /* Final status */
+
+ unsigned long inserttime; /* In jiffies */
+
+ struct list_head queue_list; /* P: xhci->frame_list_lock */
+ struct list_head complete_list; /* P: xhci->complete_list_lock */
+};
+
+/*
+ * Locking in xhci.c
+ *
+ * spinlocks are used extensively to protect the many lists and data
+ * structures we have. It's not that pretty, but it's necessary. We
+ * need to be done with all of the locks (except complete_list_lock) when
+ * we call urb->complete. I've tried to make it simple enough so I don't
+ * have to spend hours racking my brain trying to figure out if the
+ * locking is safe.
+ *
+ * Here's the safe locking order to prevent deadlocks:
+ *
+ * #1 xhci->urb_list_lock
+ * #2 urb->lock
+ * #3 xhci->urb_remove_list_lock, xhci->frame_list_lock,
+ * xhci->qh_remove_list_lock
+ * #4 xhci->complete_list_lock
+ *
+ * If you're going to grab 2 or more locks at once, ALWAYS grab the lock
+ * at the lowest level FIRST and NEVER grab locks at the same level at the
+ * same time.
+ *
+ * So, if you need xhci->urb_list_lock, grab it before you grab urb->lock
+ */
+
+/* -------------------------------------------------------------------------
+ Virtual Root HUB
+ ------------------------------------------------------------------------- */
+/* destination of request */
+#define RH_DEVICE 0x00
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP 0x00
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+#endif
+
--- /dev/null
+/******************************************************************************
+ * usbif.h
+ *
+ * Unified block-device I/O interface for Xen guest OSes.
+ *
+ * Copyright (c) 2003-2004, Keir Fraser
+ */
+
+#ifndef __SHARED_USBIF_H__
+#define __SHARED_USBIF_H__
+
+#define usbif_vdev_t u16
+#define usbif_sector_t u64
+
+#define USBIF_OP_IO 0
+#define USBIF_OP_PROBE 1 /* Is there a device on this port? */
+#define USBIF_OP_RESET 2 /* Reset a virtual USB port. */
+
+/* NB. Ring size must be small enough for sizeof(usbif_ring_t) <= PAGE_SIZE. */
+#define USBIF_RING_SIZE 64
+
+/* XXX this does not want to be here! it really ought to be dynamic but it can
+ * live here for now */
+#define NUM_PORTS 1
+
+typedef struct {
+ unsigned long id; /* 0: private guest value, echoed in resp */
+ u8 operation; /* 4: USBIF_OP_??? */
+ u8 __pad1;
+ usbif_vdev_t port; /* 6 : guest virtual USB port */
+ unsigned long devnum :7; /* 8 : Device address, as seen by the guest.*/
+ unsigned long endpoint :4; /* Device endpoint. */
+ unsigned long direction :1; /* Pipe direction. */
+ unsigned long speed :1; /* Pipe speed. */
+ unsigned long pipe_type :2; /* Pipe type (iso, bulk, int, ctrl) */
+ unsigned long __pad2 :18;
+ unsigned long transfer_buffer; /* 12: Machine address */
+ unsigned long length; /* 16: Buffer length */
+ unsigned long transfer_flags; /* 20: For now just pass Linux transfer
+ * flags - this may change. */
+ unsigned char setup[8]; /* 22 Embed setup packets directly. */
+ unsigned long iso_schedule; /* 30 Machine address of transfer sched (iso
+ * only) */
+ unsigned long num_iso; /* 34 : length of iso schedule */
+ unsigned long timeout; /* 38: timeout in ms */
+} PACKED usbif_request_t; /* 42 */
+/* Data we need to pass:
+ * - Transparently handle short packets or complain at us?
+ */
+
+typedef struct {
+ unsigned long id; /* 0: copied from request */
+ u8 operation; /* 4: copied from request */
+ u8 data; /* 5: Small chunk of in-band data */
+ s16 status; /* 6: USBIF_RSP_??? */
+ unsigned long transfer_mutex; /* Used for cancelling requests atomically. */
+ unsigned long length; /* 8: How much data we really got */
+} PACKED usbif_response_t;
+
+#define USBIF_RSP_ERROR -1 /* non-specific 'error' */
+#define USBIF_RSP_OKAY 0 /* non-specific 'okay' */
+
+/*
+ * We use a special capitalised type name because it is _essential_ that all
+ * arithmetic on indexes is done on an integer type of the correct size.
+ */
+typedef u32 USBIF_RING_IDX;
+
+/*
+ * Ring indexes are 'free running'. That is, they are not stored modulo the
+ * size of the ring buffer. The following macro converts a free-running counter
+ * into a value that can directly index a ring-buffer array.
+ */
+#define MASK_USBIF_IDX(_i) ((_i)&(USBIF_RING_SIZE-1))
+
+typedef struct {
+ USBIF_RING_IDX req_prod; /* 0: Request producer. Updated by front-end. */
+ USBIF_RING_IDX resp_prod; /* 4: Response producer. Updated by back-end. */
+
+ union { /* 8 */
+ usbif_request_t req;
+ usbif_response_t resp;
+ } PACKED ring[USBIF_RING_SIZE];
+} PACKED usbif_t;
+
+
+
+/*
+ * USBIF_OP_PROBE:
+ * The request format for a probe request is constrained as follows:
+ * @operation == USBIF_OP_PROBE
+ * @nr_segments == size of probe buffer in pages
+ * @device == unused (zero)
+ * @id == any value (echoed in response message)
+ * @sector_num == unused (zero)
+ * @frame_and_sects == list of page-sized buffers.
+ * (i.e., @first_sect == 0, @last_sect == 7).
+ *
+ * The response is a list of vdisk_t elements copied into the out-of-band
+ * probe buffer. On success the response status field contains the number
+ * of vdisk_t elements.
+ */
+
+typedef struct {
+ unsigned long length; /* IN = expected, OUT = actual */
+ unsigned long buffer_offset; /* IN offset in buffer specified in main
+ packet */
+ unsigned long status; /* OUT Status for this packet. */
+} usbif_iso_t;
+
+#endif /* __SHARED_USBIF_H__ */